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 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/UserGestureIndicator.h> 74#import <WebCore/WebCoreObjCExtras.h> 75#import <WebCore/WebCoreURLResponse.h> 76#import <WebCore/npruntime_impl.h> 77#import <WebKitLegacy/DOMPrivate.h> 78#import <WebKitLegacy/WebUIDelegate.h> 79#import <objc/runtime.h> 80#import <runtime/InitializeThreading.h> 81#import <runtime/JSLock.h> 82#import <wtf/Assertions.h> 83#import <wtf/MainThread.h> 84#import <wtf/RunLoop.h> 85#import <wtf/text/CString.h> 86 87#define LoginWindowDidSwitchFromUserNotification @"WebLoginWindowDidSwitchFromUserNotification" 88#define LoginWindowDidSwitchToUserNotification @"WebLoginWindowDidSwitchToUserNotification" 89#define WKNVSupportsCompositingCoreAnimationPluginsBool 74656 /* TRUE if the browser supports hardware compositing of Core Animation plug-ins */ 90static const int WKNVSilverlightFullscreenPerformanceIssueFixed = 7288546; /* TRUE if Siverlight addressed its underlying bug in <rdar://problem/7288546> */ 91 92using namespace WebCore; 93using namespace WebKit; 94 95static inline bool isDrawingModelQuickDraw(NPDrawingModel drawingModel) 96{ 97#ifndef NP_NO_QUICKDRAW 98 return drawingModel == NPDrawingModelQuickDraw; 99#else 100 return false; 101#endif 102}; 103 104@interface WebNetscapePluginView (Internal) 105- (NPError)_createPlugin; 106- (void)_destroyPlugin; 107- (NSBitmapImageRep *)_printedPluginBitmap; 108- (void)_redeliverStream; 109- (BOOL)_shouldCancelSrcStream; 110@end 111 112static WebNetscapePluginView *currentPluginView = nil; 113 114typedef struct OpaquePortState* PortState; 115 116static const double ThrottledTimerInterval = 0.25; 117 118class PluginTimer : public TimerBase { 119public: 120 typedef void (*TimerFunc)(NPP npp, uint32_t timerID); 121 122 PluginTimer(NPP npp, uint32_t timerID, uint32_t interval, NPBool repeat, TimerFunc timerFunc) 123 : m_npp(npp) 124 , m_timerID(timerID) 125 , m_interval(interval) 126 , m_repeat(repeat) 127 , m_timerFunc(timerFunc) 128 { 129 } 130 131 void start(bool throttle) 132 { 133 ASSERT(!isActive()); 134 135 double timeInterval = m_interval / 1000.0; 136 137 if (throttle) 138 timeInterval = std::max(timeInterval, ThrottledTimerInterval); 139 140 if (m_repeat) 141 startRepeating(timeInterval); 142 else 143 startOneShot(timeInterval); 144 } 145 146private: 147 virtual void fired() 148 { 149 m_timerFunc(m_npp, m_timerID); 150 if (!m_repeat) 151 delete this; 152 } 153 154 NPP m_npp; 155 uint32_t m_timerID; 156 uint32_t m_interval; 157 NPBool m_repeat; 158 TimerFunc m_timerFunc; 159}; 160 161#ifndef NP_NO_QUICKDRAW 162 163// QuickDraw is not available in 64-bit 164 165typedef struct { 166 GrafPtr oldPort; 167 GDHandle oldDevice; 168 Point oldOrigin; 169 RgnHandle oldClipRegion; 170 RgnHandle oldVisibleRegion; 171 RgnHandle clipRegion; 172 BOOL forUpdate; 173} PortState_QD; 174 175#endif /* NP_NO_QUICKDRAW */ 176 177typedef struct { 178 CGContextRef context; 179} PortState_CG; 180 181@class NSTextInputContext; 182@interface NSResponder (AppKitDetails) 183- (NSTextInputContext *)inputContext; 184@end 185 186@interface WebNetscapePluginView (ForwardDeclarations) 187- (void)setWindowIfNecessary; 188- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification; 189@end 190 191@implementation WebNetscapePluginView 192 193+ (void)initialize 194{ 195 JSC::initializeThreading(); 196 WTF::initializeMainThreadToProcessMainThread(); 197 RunLoop::initializeMainRunLoop(); 198 WebCoreObjCFinalizeOnMainThread(self); 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 { 671 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 672 UserGestureIndicator gestureIndicator(_eventHandler->currentEventIsUserGesture() ? DefinitelyProcessingUserGesture : PossiblyProcessingUserGesture); 673 acceptedEvent = [_pluginPackage.get() pluginFuncs]->event(plugin, event); 674 } 675 [self didCallPlugInFunction]; 676 677 if (portState) { 678 if ([self currentWindow]) 679 [self restorePortState:portState]; 680 if (portState != (PortState)1) 681 free(portState); 682 } 683 684 return acceptedEvent; 685} 686 687- (void)windowFocusChanged:(BOOL)hasFocus 688{ 689 _eventHandler->windowFocusChanged(hasFocus); 690} 691 692- (void)sendDrawRectEvent:(NSRect)rect 693{ 694 ASSERT(_eventHandler); 695 696 CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]); 697 _eventHandler->drawRect(context, rect); 698} 699 700- (void)stopTimers 701{ 702 [super stopTimers]; 703 704 if (_eventHandler) 705 _eventHandler->stopTimers(); 706 707 if (!timers) 708 return; 709 710 for (auto& it: timers->values()) 711 it->stop(); 712} 713 714- (void)startTimers 715{ 716 [super startTimers]; 717 718 // If the plugin is completely obscured (scrolled out of view, for example), then we will 719 // send null events at a reduced rate. 720 _eventHandler->startTimers(_isCompletelyObscured); 721 722 if (!timers) 723 return; 724 725 for (auto& it: timers->values()) { 726 ASSERT(!it->isActive()); 727 it->start(_isCompletelyObscured); 728 } 729} 730 731- (void)focusChanged 732{ 733 // We need to null check the event handler here because 734 // the plug-in view can resign focus after it's been stopped 735 // and the event handler has been deleted. 736 if (_eventHandler) 737 _eventHandler->focusChanged(_hasFocus); 738} 739 740- (void)mouseDown:(NSEvent *)theEvent 741{ 742 if (!_isStarted) 743 return; 744 745 _eventHandler->mouseDown(theEvent); 746} 747 748- (void)mouseUp:(NSEvent *)theEvent 749{ 750 if (!_isStarted) 751 return; 752 753 _eventHandler->mouseUp(theEvent); 754} 755 756- (void)handleMouseEntered:(NSEvent *)theEvent 757{ 758 if (!_isStarted) 759 return; 760 761 // Set cursor to arrow. Plugins often handle cursor internally, but those that don't will just get this default one. 762 [[NSCursor arrowCursor] set]; 763 764 _eventHandler->mouseEntered(theEvent); 765} 766 767- (void)handleMouseExited:(NSEvent *)theEvent 768{ 769 if (!_isStarted) 770 return; 771 772 _eventHandler->mouseExited(theEvent); 773 774 // 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 775 // current cursor is otherwise. Therefore we have no choice but to unconditionally reset the cursor when the mouse exits the plugin. 776 [[NSCursor arrowCursor] set]; 777} 778 779- (void)handleMouseMoved:(NSEvent *)theEvent 780{ 781 if (!_isStarted) 782 return; 783 784 _eventHandler->mouseMoved(theEvent); 785} 786 787- (void)mouseDragged:(NSEvent *)theEvent 788{ 789 if (!_isStarted) 790 return; 791 792 _eventHandler->mouseDragged(theEvent); 793} 794 795- (void)scrollWheel:(NSEvent *)theEvent 796{ 797 if (!_isStarted) { 798 [super scrollWheel:theEvent]; 799 return; 800 } 801 802 if (!_eventHandler->scrollWheel(theEvent)) 803 [super scrollWheel:theEvent]; 804} 805 806- (void)keyUp:(NSEvent *)theEvent 807{ 808 if (!_isStarted) 809 return; 810 811 _eventHandler->keyUp(theEvent); 812} 813 814- (void)keyDown:(NSEvent *)theEvent 815{ 816 if (!_isStarted) 817 return; 818 819 _eventHandler->keyDown(theEvent); 820} 821 822- (void)flagsChanged:(NSEvent *)theEvent 823{ 824 if (!_isStarted) 825 return; 826 827 _eventHandler->flagsChanged(theEvent); 828} 829 830- (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character 831{ 832 if (!_isStarted) 833 return; 834 835 _eventHandler->syntheticKeyDownWithCommandModifier(keyCode, character); 836} 837 838- (void)privateBrowsingModeDidChange 839{ 840 if (!_isStarted) 841 return; 842 843 NPBool value = _isPrivateBrowsingEnabled; 844 845 [self willCallPlugInFunction]; 846 { 847 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 848 if ([_pluginPackage.get() pluginFuncs]->setvalue) 849 [_pluginPackage.get() pluginFuncs]->setvalue(plugin, NPNVprivateModeBool, &value); 850 } 851 [self didCallPlugInFunction]; 852} 853 854// MARK: WEB_NETSCAPE_PLUGIN 855 856- (BOOL)isNewWindowEqualToOldWindow 857{ 858 if (window.x != lastSetWindow.x) 859 return NO; 860 if (window.y != lastSetWindow.y) 861 return NO; 862 if (window.width != lastSetWindow.width) 863 return NO; 864 if (window.height != lastSetWindow.height) 865 return NO; 866 if (window.clipRect.top != lastSetWindow.clipRect.top) 867 return NO; 868 if (window.clipRect.left != lastSetWindow.clipRect.left) 869 return NO; 870 if (window.clipRect.bottom != lastSetWindow.clipRect.bottom) 871 return NO; 872 if (window.clipRect.right != lastSetWindow.clipRect.right) 873 return NO; 874 if (window.type != lastSetWindow.type) 875 return NO; 876 877 switch (drawingModel) { 878#ifndef NP_NO_QUICKDRAW 879 case NPDrawingModelQuickDraw: 880 if (nPort.qdPort.portx != lastSetPort.qdPort.portx) 881 return NO; 882 if (nPort.qdPort.porty != lastSetPort.qdPort.porty) 883 return NO; 884 if (nPort.qdPort.port != lastSetPort.qdPort.port) 885 return NO; 886 break; 887#endif /* NP_NO_QUICKDRAW */ 888 889 case NPDrawingModelCoreGraphics: 890 if (nPort.cgPort.window != lastSetPort.cgPort.window) 891 return NO; 892 if (nPort.cgPort.context != lastSetPort.cgPort.context) 893 return NO; 894 break; 895 896 case NPDrawingModelCoreAnimation: 897 if (window.window != lastSetWindow.window) 898 return NO; 899 break; 900 default: 901 ASSERT_NOT_REACHED(); 902 break; 903 } 904 905 return YES; 906} 907 908-(void)tellQuickTimeToChill 909{ 910#ifndef NP_NO_QUICKDRAW 911 ASSERT(isDrawingModelQuickDraw(drawingModel)); 912 913 // Make a call to the secret QuickDraw API that makes QuickTime calm down. 914 WindowRef windowRef = (WindowRef)[[self window] windowRef]; 915 if (!windowRef) { 916 return; 917 } 918 CGrafPtr port = GetWindowPort(windowRef); 919 ::Rect bounds; 920 GetPortBounds(port, &bounds); 921 WKCallDrawingNotification(port, &bounds); 922#endif /* NP_NO_QUICKDRAW */ 923} 924 925- (void)updateAndSetWindow 926{ 927 // A plug-in can only update if it's (1) already been started (2) isn't stopped 928 // and (3) is able to draw on-screen. To meet condition (3) the plug-in must not 929 // be hidden and be attached to a window. There are two exceptions to this rule: 930 // 931 // Exception 1: QuickDraw plug-ins must be manually told when to stop writing 932 // bits to the window backing store, thus to do so requires a new call to 933 // NPP_SetWindow() with an empty NPWindow struct. 934 // 935 // Exception 2: CoreGraphics plug-ins expect to have their drawable area updated 936 // when they are moved to a background tab, via a NPP_SetWindow call. This is 937 // accomplished by allowing -saveAndSetNewPortStateForUpdate to "clip-out" the window's 938 // clipRect. Flash is curently an exception to this. See 6453738. 939 // 940 941 if (!_isStarted) 942 return; 943 944#ifdef NP_NO_QUICKDRAW 945 if (![self canDraw]) 946 return; 947#else 948 if (drawingModel == NPDrawingModelQuickDraw) 949 [self tellQuickTimeToChill]; 950 else if (drawingModel == NPDrawingModelCoreGraphics && ![self canDraw] && _isFlash) { 951 // The Flash plug-in does not expect an NPP_SetWindow call from WebKit in this case. 952 // See Exception 2 above. 953 return; 954 } 955#endif // NP_NO_QUICKDRAW 956 957 BOOL didLockFocus = [NSView focusView] != self && [self lockFocusIfCanDraw]; 958 959 PortState portState = [self saveAndSetNewPortState]; 960 if (portState) { 961 [self setWindowIfNecessary]; 962 [self restorePortState:portState]; 963 if (portState != (PortState)1) 964 free(portState); 965 } else if (drawingModel == NPDrawingModelCoreGraphics) 966 [self setWindowIfNecessary]; 967 968 if (didLockFocus) 969 [self unlockFocus]; 970} 971 972- (void)setWindowIfNecessary 973{ 974 if (!_isStarted) 975 return; 976 977 if (![self isNewWindowEqualToOldWindow]) { 978 // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow. 979 // We probably don't want more general reentrancy protection; we are really 980 // protecting only against this one case, which actually comes up when 981 // you first install the SVG viewer plug-in. 982 NPError npErr; 983 984 BOOL wasInSetWindow = inSetWindow; 985 inSetWindow = YES; 986 [self willCallPlugInFunction]; 987 { 988 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 989 npErr = [_pluginPackage.get() pluginFuncs]->setwindow(plugin, &window); 990 } 991 [self didCallPlugInFunction]; 992 inSetWindow = wasInSetWindow; 993 994#ifndef NDEBUG 995 switch (drawingModel) { 996#ifndef NP_NO_QUICKDRAW 997 case NPDrawingModelQuickDraw: 998 LOG(Plugins, "NPP_SetWindow (QuickDraw): %d, port=0x%08x, window.x:%d window.y:%d window.width:%d window.height:%d", 999 npErr, (int)nPort.qdPort.port, (int)window.x, (int)window.y, (int)window.width, (int)window.height); 1000 break; 1001#endif /* NP_NO_QUICKDRAW */ 1002 1003 case NPDrawingModelCoreGraphics: 1004 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", 1005 npErr, nPort.cgPort.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height, 1006 window.clipRect.right - window.clipRect.left, window.clipRect.bottom - window.clipRect.top); 1007 break; 1008 1009 case NPDrawingModelCoreAnimation: 1010 LOG(Plugins, "NPP_SetWindow (CoreAnimation): %d, window=%p window.x:%d window.y:%d window.width:%d window.height:%d", 1011 npErr, window.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height); 1012 break; 1013 1014 default: 1015 ASSERT_NOT_REACHED(); 1016 break; 1017 } 1018#endif /* !defined(NDEBUG) */ 1019 1020 lastSetWindow = window; 1021 lastSetPort = nPort; 1022 } 1023} 1024 1025+ (void)setCurrentPluginView:(WebNetscapePluginView *)view 1026{ 1027 currentPluginView = view; 1028} 1029 1030+ (WebNetscapePluginView *)currentPluginView 1031{ 1032 return currentPluginView; 1033} 1034 1035- (BOOL)createPlugin 1036{ 1037 // Open the plug-in package so it remains loaded while our plugin uses it 1038 [_pluginPackage.get() open]; 1039 1040 // Initialize drawingModel to an invalid value so that we can detect when the plugin does not specify a drawingModel 1041 drawingModel = (NPDrawingModel)-1; 1042 1043 // Initialize eventModel to an invalid value so that we can detect when the plugin does not specify an event model. 1044 eventModel = (NPEventModel)-1; 1045 1046 NPError npErr = [self _createPlugin]; 1047 if (npErr != NPERR_NO_ERROR) { 1048 LOG_ERROR("NPP_New failed with error: %d", npErr); 1049 [self _destroyPlugin]; 1050 [_pluginPackage.get() close]; 1051 return NO; 1052 } 1053 1054 if (drawingModel == (NPDrawingModel)-1) { 1055#ifndef NP_NO_QUICKDRAW 1056 // Default to QuickDraw if the plugin did not specify a drawing model. 1057 drawingModel = NPDrawingModelQuickDraw; 1058#else 1059 // QuickDraw is not available, so we can't default to it. Instead, default to CoreGraphics. 1060 drawingModel = NPDrawingModelCoreGraphics; 1061#endif 1062 } 1063 1064 if (eventModel == (NPEventModel)-1) { 1065 // If the plug-in did not specify a drawing model we default to Carbon when it is available. 1066#ifndef NP_NO_CARBON 1067 eventModel = NPEventModelCarbon; 1068#else 1069 eventModel = NPEventModelCocoa; 1070#endif // NP_NO_CARBON 1071 } 1072 1073#ifndef NP_NO_CARBON 1074 if (eventModel == NPEventModelCocoa && isDrawingModelQuickDraw(drawingModel)) { 1075 LOG(Plugins, "Plugin can't use use Cocoa event model with QuickDraw drawing model: %@", _pluginPackage.get()); 1076 [self _destroyPlugin]; 1077 [_pluginPackage.get() close]; 1078 1079 return NO; 1080 } 1081#endif // NP_NO_CARBON 1082 1083 if (drawingModel == NPDrawingModelCoreAnimation) { 1084 void *value = 0; 1085 if ([_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginCoreAnimationLayer, &value) == NPERR_NO_ERROR && value) { 1086 1087 // The plug-in gives us a retained layer. 1088 _pluginLayer = adoptNS((CALayer *)value); 1089 1090 BOOL accleratedCompositingEnabled = false; 1091 accleratedCompositingEnabled = [[[self webView] preferences] acceleratedCompositingEnabled]; 1092 if (accleratedCompositingEnabled) { 1093 // FIXME: This code can be shared between WebHostedNetscapePluginView and WebNetscapePluginView. 1094 // Since this layer isn't going to be inserted into a view, we need to create another layer and flip its geometry 1095 // in order to get the coordinate system right. 1096 RetainPtr<CALayer> realPluginLayer = adoptNS(_pluginLayer.leakRef()); 1097 1098 _pluginLayer = adoptNS([[CALayer alloc] init]); 1099 _pluginLayer.get().bounds = realPluginLayer.get().bounds; 1100 _pluginLayer.get().geometryFlipped = YES; 1101 1102 realPluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; 1103 [_pluginLayer.get() addSublayer:realPluginLayer.get()]; 1104 1105 // Eagerly enter compositing mode, since we know we'll need it. This avoids firing setNeedsStyleRecalc() 1106 // for iframes that contain composited plugins at bad times. https://bugs.webkit.org/show_bug.cgi?id=39033 1107 core([self webFrame])->view()->enterCompositingMode(); 1108 [self element]->setNeedsStyleRecalc(SyntheticStyleChange); 1109 } else 1110 [self setWantsLayer:YES]; 1111 1112 LOG(Plugins, "%@ is using Core Animation drawing model with layer %@", _pluginPackage.get(), _pluginLayer.get()); 1113 } 1114 1115 ASSERT(_pluginLayer); 1116 } 1117 1118 // Create the event handler 1119 _eventHandler = WebNetscapePluginEventHandler::create(self); 1120 1121 return YES; 1122} 1123 1124// FIXME: This method is an ideal candidate to move up to the base class 1125- (CALayer *)pluginLayer 1126{ 1127 return _pluginLayer.get(); 1128} 1129 1130- (void)setLayer:(CALayer *)newLayer 1131{ 1132 [super setLayer:newLayer]; 1133 1134 if (newLayer && _pluginLayer) { 1135 _pluginLayer.get().frame = [newLayer frame]; 1136 _pluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; 1137 [newLayer addSublayer:_pluginLayer.get()]; 1138 } 1139} 1140 1141- (void)loadStream 1142{ 1143 if ([self _shouldCancelSrcStream]) 1144 return; 1145 1146 if (_loadManually) { 1147 [self _redeliverStream]; 1148 return; 1149 } 1150 1151 // If the OBJECT/EMBED tag has no SRC, the URL is passed to us as "". 1152 // Check for this and don't start a load in this case. 1153 if (_sourceURL && ![_sourceURL.get() _web_isEmpty]) { 1154 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_sourceURL.get()]; 1155 [request _web_setHTTPReferrer:core([self webFrame])->loader().outgoingReferrer()]; 1156 [self loadRequest:request inTarget:nil withNotifyData:nil sendNotification:NO]; 1157 } 1158} 1159 1160- (BOOL)shouldStop 1161{ 1162 // If we're already calling a plug-in function, do not call NPP_Destroy(). The plug-in function we are calling 1163 // may assume that its instance->pdata, or other memory freed by NPP_Destroy(), is valid and unchanged until said 1164 // plugin-function returns. 1165 // See <rdar://problem/4480737>. 1166 if (pluginFunctionCallDepth > 0) { 1167 shouldStopSoon = YES; 1168 return NO; 1169 } 1170 1171 return YES; 1172} 1173 1174- (void)destroyPlugin 1175{ 1176 // To stop active streams it's necessary to invoke stop() on a copy 1177 // of streams. This is because calling WebNetscapePluginStream::stop() also has the side effect 1178 // of removing a stream from this hash set. 1179 Vector<RefPtr<WebNetscapePluginStream>> streamsCopy; 1180 copyToVector(streams, streamsCopy); 1181 for (auto& stream: streamsCopy) 1182 stream->stop(); 1183 1184 for (WebFrame *frame in [_pendingFrameLoads keyEnumerator]) 1185 [frame _setInternalLoadDelegate:nil]; 1186 [NSObject cancelPreviousPerformRequestsWithTarget:self]; 1187 1188 // Setting the window type to 0 ensures that NPP_SetWindow will be called if the plug-in is restarted. 1189 lastSetWindow.type = (NPWindowType)0; 1190 1191 _pluginLayer = 0; 1192 1193 [self _destroyPlugin]; 1194 [_pluginPackage.get() close]; 1195 1196 _eventHandler.clear(); 1197} 1198 1199- (NPEventModel)eventModel 1200{ 1201 return eventModel; 1202} 1203 1204- (NPP)plugin 1205{ 1206 return plugin; 1207} 1208 1209- (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values 1210{ 1211 ASSERT([keys count] == [values count]); 1212 1213 // Convert the attributes to 2 C string arrays. 1214 // These arrays are passed to NPP_New, but the strings need to be 1215 // modifiable and live the entire life of the plugin. 1216 1217 // The Java plug-in requires the first argument to be the base URL 1218 if ([_MIMEType.get() isEqualToString:@"application/x-java-applet"]) { 1219 cAttributes = (char **)malloc(([keys count] + 1) * sizeof(char *)); 1220 cValues = (char **)malloc(([values count] + 1) * sizeof(char *)); 1221 cAttributes[0] = strdup("DOCBASE"); 1222 cValues[0] = strdup([_baseURL.get() _web_URLCString]); 1223 argsCount++; 1224 } else { 1225 cAttributes = (char **)malloc([keys count] * sizeof(char *)); 1226 cValues = (char **)malloc([values count] * sizeof(char *)); 1227 } 1228 1229 BOOL isWMP = [_pluginPackage.get() bundleIdentifier] == "com.microsoft.WMP.defaultplugin"; 1230 1231 unsigned i; 1232 unsigned count = [keys count]; 1233 for (i = 0; i < count; i++) { 1234 NSString *key = [keys objectAtIndex:i]; 1235 NSString *value = [values objectAtIndex:i]; 1236 if ([key _webkit_isCaseInsensitiveEqualToString:@"height"]) { 1237 specifiedHeight = [value intValue]; 1238 } else if ([key _webkit_isCaseInsensitiveEqualToString:@"width"]) { 1239 specifiedWidth = [value intValue]; 1240 } 1241 // Avoid Window Media Player crash when these attributes are present. 1242 if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) { 1243 continue; 1244 } 1245 cAttributes[argsCount] = strdup([key UTF8String]); 1246 cValues[argsCount] = strdup([value UTF8String]); 1247 LOG(Plugins, "%@ = %@", key, value); 1248 argsCount++; 1249 } 1250} 1251 1252- (uint32_t)checkIfAllowedToLoadURL:(const char*)urlCString frame:(const char*)frameNameCString 1253 callbackFunc:(void (*)(NPP npp, uint32_t checkID, NPBool allowed, void* context))callbackFunc 1254 context:(void*)context 1255{ 1256 if (!_containerChecksInProgress) 1257 _containerChecksInProgress = [[NSMutableDictionary alloc] init]; 1258 1259 NSString *frameName = frameNameCString ? [NSString stringWithCString:frameNameCString encoding:NSISOLatin1StringEncoding] : nil; 1260 1261 ++_currentContainerCheckRequestID; 1262 WebNetscapeContainerCheckContextInfo *contextInfo = [[WebNetscapeContainerCheckContextInfo alloc] initWithCheckRequestID:_currentContainerCheckRequestID 1263 callbackFunc:callbackFunc 1264 context:context]; 1265 1266 WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:[self requestWithURLCString:urlCString] 1267 target:frameName 1268 resultObject:self 1269 selector:@selector(_containerCheckResult:contextInfo:) 1270 controller:self 1271 contextInfo:contextInfo]; 1272 1273 [contextInfo release]; 1274 [_containerChecksInProgress setObject:check forKey:[NSNumber numberWithInt:_currentContainerCheckRequestID]]; 1275 [check start]; 1276 1277 return _currentContainerCheckRequestID; 1278} 1279 1280- (void)_containerCheckResult:(PolicyAction)policy contextInfo:(id)contextInfo 1281{ 1282 ASSERT([contextInfo isKindOfClass:[WebNetscapeContainerCheckContextInfo class]]); 1283 void (*pluginCallback)(NPP npp, uint32_t, NPBool, void*) = [contextInfo callback]; 1284 1285 if (!pluginCallback) { 1286 ASSERT_NOT_REACHED(); 1287 return; 1288 } 1289 1290 pluginCallback([self plugin], [contextInfo checkRequestID], (policy == PolicyUse), [contextInfo context]); 1291} 1292 1293- (void)cancelCheckIfAllowedToLoadURL:(uint32_t)checkID 1294{ 1295 WebPluginContainerCheck *check = (WebPluginContainerCheck *)[_containerChecksInProgress objectForKey:[NSNumber numberWithInt:checkID]]; 1296 1297 if (!check) 1298 return; 1299 1300 [check cancel]; 1301 [_containerChecksInProgress removeObjectForKey:[NSNumber numberWithInt:checkID]]; 1302} 1303 1304// WebPluginContainerCheck automatically calls this method after invoking our _containerCheckResult: selector. 1305// It works this way because calling -[WebPluginContainerCheck cancel] allows it to do it's teardown process. 1306- (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)webPluginContainerCheck 1307{ 1308 ASSERT([webPluginContainerCheck isKindOfClass:[WebPluginContainerCheck class]]); 1309 WebPluginContainerCheck *check = (WebPluginContainerCheck *)webPluginContainerCheck; 1310 ASSERT([[check contextInfo] isKindOfClass:[WebNetscapeContainerCheckContextInfo class]]); 1311 1312 [self cancelCheckIfAllowedToLoadURL:[[check contextInfo] checkRequestID]]; 1313} 1314 1315 1316// MARK: NSVIEW 1317 1318- (id)initWithFrame:(NSRect)frame 1319 pluginPackage:(WebNetscapePluginPackage *)pluginPackage 1320 URL:(NSURL *)URL 1321 baseURL:(NSURL *)baseURL 1322 MIMEType:(NSString *)MIME 1323 attributeKeys:(NSArray *)keys 1324 attributeValues:(NSArray *)values 1325 loadManually:(BOOL)loadManually 1326 element:(PassRefPtr<WebCore::HTMLPlugInElement>)element 1327{ 1328 self = [super initWithFrame:frame pluginPackage:pluginPackage URL:URL baseURL:baseURL MIMEType:MIME attributeKeys:keys attributeValues:values loadManually:loadManually element:element]; 1329 if (!self) 1330 return nil; 1331 1332 _pendingFrameLoads = adoptNS([[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsStrongMemory capacity:0]); 1333 1334 // load the plug-in if it is not already loaded 1335 if (![pluginPackage load]) { 1336 [self release]; 1337 return nil; 1338 } 1339 1340 return self; 1341} 1342 1343- (id)initWithFrame:(NSRect)frame 1344{ 1345 ASSERT_NOT_REACHED(); 1346 return nil; 1347} 1348 1349- (void)fini 1350{ 1351#ifndef NP_NO_QUICKDRAW 1352 if (offscreenGWorld) 1353 DisposeGWorld(offscreenGWorld); 1354#endif 1355 1356 for (unsigned i = 0; i < argsCount; i++) { 1357 free(cAttributes[i]); 1358 free(cValues[i]); 1359 } 1360 free(cAttributes); 1361 free(cValues); 1362 1363 ASSERT(!_eventHandler); 1364 1365 [_containerChecksInProgress release]; 1366} 1367 1368- (void)disconnectStream:(WebNetscapePluginStream*)stream 1369{ 1370 streams.remove(stream); 1371} 1372 1373- (void)dealloc 1374{ 1375 ASSERT(!_isStarted); 1376 ASSERT(!plugin); 1377 1378 [self fini]; 1379 1380 [super dealloc]; 1381} 1382 1383- (void)finalize 1384{ 1385 ASSERT_MAIN_THREAD(); 1386 ASSERT(!_isStarted); 1387 1388 [self fini]; 1389 1390 [super finalize]; 1391} 1392 1393- (void)drawRect:(NSRect)rect 1394{ 1395 if (_cachedSnapshot) { 1396 NSRect sourceRect = { NSZeroPoint, [_cachedSnapshot.get() size] }; 1397 [_cachedSnapshot.get() drawInRect:[self bounds] fromRect:sourceRect operation:NSCompositeSourceOver fraction:1]; 1398 return; 1399 } 1400 1401 if (drawingModel == NPDrawingModelCoreAnimation && (!_snapshotting || ![self supportsSnapshotting])) 1402 return; 1403 1404 if (!_isStarted) 1405 return; 1406 1407 if ([NSGraphicsContext currentContextDrawingToScreen] || _isFlash) 1408 [self sendDrawRectEvent:rect]; 1409 else { 1410 NSBitmapImageRep *printedPluginBitmap = [self _printedPluginBitmap]; 1411 if (printedPluginBitmap) { 1412 // Flip the bitmap before drawing because the QuickDraw port is flipped relative 1413 // to this view. 1414 CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; 1415 CGContextSaveGState(cgContext); 1416 NSRect bounds = [self bounds]; 1417 CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds)); 1418 CGContextScaleCTM(cgContext, 1.0f, -1.0f); 1419 [printedPluginBitmap drawInRect:bounds]; 1420 CGContextRestoreGState(cgContext); 1421 } 1422 } 1423} 1424 1425- (NPObject *)createPluginScriptableObject 1426{ 1427 if (![_pluginPackage.get() pluginFuncs]->getvalue || !_isStarted) 1428 return NULL; 1429 1430 NPObject *value = NULL; 1431 NPError error; 1432 [self willCallPlugInFunction]; 1433 { 1434 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 1435 error = [_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginScriptableNPObject, &value); 1436 } 1437 [self didCallPlugInFunction]; 1438 if (error != NPERR_NO_ERROR) 1439 return NULL; 1440 1441 return value; 1442} 1443 1444- (BOOL)getFormValue:(NSString **)value 1445{ 1446 if (![_pluginPackage.get() pluginFuncs]->getvalue || !_isStarted) 1447 return false; 1448 // Plugins will allocate memory for the buffer by using NPN_MemAlloc(). 1449 char* buffer = NULL; 1450 NPError error; 1451 [self willCallPlugInFunction]; 1452 { 1453 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 1454 error = [_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVformValue, &buffer); 1455 } 1456 [self didCallPlugInFunction]; 1457 if (error != NPERR_NO_ERROR || !buffer) 1458 return false; 1459 *value = [[NSString alloc] initWithUTF8String:buffer]; 1460 [_pluginPackage.get() browserFuncs]->memfree(buffer); 1461 return true; 1462} 1463 1464- (void)willCallPlugInFunction 1465{ 1466 ASSERT(plugin); 1467 1468 // Could try to prevent infinite recursion here, but it's probably not worth the effort. 1469 pluginFunctionCallDepth++; 1470} 1471 1472- (void)didCallPlugInFunction 1473{ 1474 ASSERT(pluginFunctionCallDepth > 0); 1475 pluginFunctionCallDepth--; 1476 1477 // If -stop was called while we were calling into a plug-in function, and we're no longer 1478 // inside a plug-in function, stop now. 1479 if (pluginFunctionCallDepth == 0 && shouldStopSoon) { 1480 shouldStopSoon = NO; 1481 [self stop]; 1482 } 1483} 1484 1485-(void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response 1486{ 1487 ASSERT(_loadManually); 1488 ASSERT(!_manualStream); 1489 1490 _manualStream = WebNetscapePluginStream::create(&core([self webFrame])->loader()); 1491} 1492 1493- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data 1494{ 1495 ASSERT(_loadManually); 1496 ASSERT(_manualStream); 1497 1498 _dataLengthReceived += [data length]; 1499 1500 if (!_isStarted) 1501 return; 1502 1503 if (!_manualStream->plugin()) { 1504 // Check if the load should be cancelled 1505 if ([self _shouldCancelSrcStream]) { 1506 NSURLResponse *response = [[self dataSource] response]; 1507 1508 NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad 1509 contentURL:[response URL] 1510 pluginPageURL:nil 1511 pluginName:nil // FIXME: Get this from somewhere 1512 MIMEType:[response MIMEType]]; 1513 [[self dataSource] _documentLoader]->cancelMainResourceLoad(error); 1514 [error release]; 1515 return; 1516 } 1517 1518 _manualStream->setRequestURL([[[self dataSource] request] URL]); 1519 _manualStream->setPlugin([self plugin]); 1520 ASSERT(_manualStream->plugin()); 1521 1522 _manualStream->startStreamWithResponse([[self dataSource] response]); 1523 } 1524 1525 if (_manualStream->plugin()) 1526 _manualStream->didReceiveData(0, static_cast<const char *>([data bytes]), [data length]); 1527} 1528 1529- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error 1530{ 1531 ASSERT(_loadManually); 1532 1533 _error = error; 1534 1535 if (!_isStarted) { 1536 return; 1537 } 1538 1539 _manualStream->destroyStreamWithError(error); 1540} 1541 1542- (void)pluginViewFinishedLoading:(NSView *)pluginView 1543{ 1544 ASSERT(_loadManually); 1545 ASSERT(_manualStream); 1546 1547 if (_isStarted) 1548 _manualStream->didFinishLoading(0); 1549} 1550 1551- (NSTextInputContext *)inputContext 1552{ 1553 return nil; 1554} 1555 1556@end 1557 1558@implementation WebNetscapePluginView (WebNPPCallbacks) 1559 1560- (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest 1561{ 1562 // FIXME: Is this isStarted check needed here? evaluateJavaScriptPluginRequest should not be called 1563 // if we are stopped since this method is called after a delay and we call 1564 // cancelPreviousPerformRequestsWithTarget inside of stop. 1565 if (!_isStarted) { 1566 return; 1567 } 1568 1569 NSURL *URL = [[JSPluginRequest request] URL]; 1570 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; 1571 ASSERT(JSString); 1572 1573 NSString *result = [[self webFrame] _stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]]; 1574 1575 // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop. 1576 if (!_isStarted) { 1577 return; 1578 } 1579 1580 if ([JSPluginRequest frameName] != nil) { 1581 // FIXME: If the result is a string, we probably want to put that string into the frame. 1582 if ([JSPluginRequest sendNotification]) { 1583 [self willCallPlugInFunction]; 1584 { 1585 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 1586 [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]); 1587 } 1588 [self didCallPlugInFunction]; 1589 } 1590 } else if ([result length] > 0) { 1591 // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does. 1592 NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding]; 1593 1594 RefPtr<WebNetscapePluginStream> stream = WebNetscapePluginStream::create([NSURLRequest requestWithURL:URL], plugin, [JSPluginRequest sendNotification], [JSPluginRequest notifyData]); 1595 1596 RetainPtr<NSURLResponse> response = adoptNS([[NSURLResponse alloc] initWithURL:URL 1597 MIMEType:@"text/plain" 1598 expectedContentLength:[JSData length] 1599 textEncodingName:nil]); 1600 1601 stream->startStreamWithResponse(response.get()); 1602 stream->didReceiveData(0, static_cast<const char*>([JSData bytes]), [JSData length]); 1603 stream->didFinishLoading(0); 1604 } 1605} 1606 1607- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason 1608{ 1609 ASSERT(_isStarted); 1610 1611 WebPluginRequest *pluginRequest = [_pendingFrameLoads objectForKey:webFrame]; 1612 ASSERT(pluginRequest != nil); 1613 ASSERT([pluginRequest sendNotification]); 1614 1615 [self willCallPlugInFunction]; 1616 { 1617 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 1618 [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]); 1619 } 1620 [self didCallPlugInFunction]; 1621 1622 [_pendingFrameLoads removeObjectForKey:webFrame]; 1623 [webFrame _setInternalLoadDelegate:nil]; 1624} 1625 1626- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error 1627{ 1628 NPReason reason = NPRES_DONE; 1629 if (error != nil) 1630 reason = WebNetscapePluginStream::reasonForError(error); 1631 [self webFrame:webFrame didFinishLoadWithReason:reason]; 1632} 1633 1634- (void)loadPluginRequest:(WebPluginRequest *)pluginRequest 1635{ 1636 NSURLRequest *request = [pluginRequest request]; 1637 NSString *frameName = [pluginRequest frameName]; 1638 WebFrame *frame = nil; 1639 1640 NSURL *URL = [request URL]; 1641 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; 1642 1643 ASSERT(frameName || JSString); 1644 1645 if (frameName) { 1646 // FIXME - need to get rid of this window creation which 1647 // bypasses normal targeted link handling 1648 frame = kit(core([self webFrame])->loader().findFrameForNavigation(frameName)); 1649 if (frame == nil) { 1650 WebView *currentWebView = [self webView]; 1651 NSDictionary *features = [[NSDictionary alloc] init]; 1652 WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView 1653 createWebViewWithRequest:nil 1654 windowFeatures:features]; 1655 [features release]; 1656 1657 if (!newWebView) { 1658 if ([pluginRequest sendNotification]) { 1659 [self willCallPlugInFunction]; 1660 { 1661 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 1662 [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [[[pluginRequest request] URL] _web_URLCString], NPERR_GENERIC_ERROR, [pluginRequest notifyData]); 1663 } 1664 [self didCallPlugInFunction]; 1665 } 1666 return; 1667 } 1668 1669 frame = [newWebView mainFrame]; 1670 core(frame)->tree().setName(frameName); 1671 [[newWebView _UIDelegateForwarder] webViewShow:newWebView]; 1672 } 1673 } 1674 1675 if (JSString) { 1676 ASSERT(frame == nil || [self webFrame] == frame); 1677 [self evaluateJavaScriptPluginRequest:pluginRequest]; 1678 } else { 1679 [frame loadRequest:request]; 1680 if ([pluginRequest sendNotification]) { 1681 // Check if another plug-in view or even this view is waiting for the frame to load. 1682 // If it is, tell it that the load was cancelled because it will be anyway. 1683 WebNetscapePluginView *view = [frame _internalLoadDelegate]; 1684 if (view != nil) { 1685 ASSERT([view isKindOfClass:[WebNetscapePluginView class]]); 1686 [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK]; 1687 } 1688 [_pendingFrameLoads setObject:pluginRequest forKey:frame]; 1689 [frame _setInternalLoadDelegate:self]; 1690 } 1691 } 1692} 1693 1694- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification 1695{ 1696 NSURL *URL = [request URL]; 1697 1698 if (!URL) 1699 return NPERR_INVALID_URL; 1700 1701 // Don't allow requests to be loaded when the document loader is stopping all loaders. 1702 if ([[self dataSource] _documentLoader]->isStopping()) 1703 return NPERR_GENERIC_ERROR; 1704 1705 NSString *target = nil; 1706 if (cTarget) { 1707 // Find the frame given the target string. 1708 target = [NSString stringWithCString:cTarget encoding:NSISOLatin1StringEncoding]; 1709 } 1710 WebFrame *frame = [self webFrame]; 1711 1712 // don't let a plugin start any loads if it is no longer part of a document that is being 1713 // displayed unless the loads are in the same frame as the plugin. 1714 if ([[self dataSource] _documentLoader] != core([self webFrame])->loader().activeDocumentLoader() && 1715 (!cTarget || [frame findFrameNamed:target] != frame)) { 1716 return NPERR_GENERIC_ERROR; 1717 } 1718 1719 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; 1720 if (JSString != nil) { 1721 if (![[[self webView] preferences] isJavaScriptEnabled]) { 1722 // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does. 1723 return NPERR_GENERIC_ERROR; 1724 } else if (cTarget == NULL && _mode == NP_FULL) { 1725 // Don't allow a JavaScript request from a standalone plug-in that is self-targetted 1726 // because this can cause the user to be redirected to a blank page (3424039). 1727 return NPERR_INVALID_PARAM; 1728 } 1729 } else { 1730 if (!core([self webFrame])->document()->securityOrigin()->canDisplay(URL)) 1731 return NPERR_GENERIC_ERROR; 1732 } 1733 1734 if (cTarget || JSString) { 1735 // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't 1736 // want to potentially kill the plug-in inside of its URL request. 1737 1738 if (JSString && target && [frame findFrameNamed:target] != frame) { 1739 // For security reasons, only allow JS requests to be made on the frame that contains the plug-in. 1740 return NPERR_INVALID_PARAM; 1741 } 1742 1743 bool currentEventIsUserGesture = false; 1744 if (_eventHandler) 1745 currentEventIsUserGesture = _eventHandler->currentEventIsUserGesture(); 1746 1747 WebPluginRequest *pluginRequest = [[WebPluginRequest alloc] initWithRequest:request 1748 frameName:target 1749 notifyData:notifyData 1750 sendNotification:sendNotification 1751 didStartFromUserGesture:currentEventIsUserGesture]; 1752 [self performSelector:@selector(loadPluginRequest:) withObject:pluginRequest afterDelay:0]; 1753 [pluginRequest release]; 1754 } else { 1755 RefPtr<WebNetscapePluginStream> stream = WebNetscapePluginStream::create(request, plugin, sendNotification, notifyData); 1756 1757 streams.add(stream.get()); 1758 stream->start(); 1759 } 1760 1761 return NPERR_NO_ERROR; 1762} 1763 1764-(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData 1765{ 1766 LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget); 1767 1768 NSMutableURLRequest *request = [self requestWithURLCString:URLCString]; 1769 return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES]; 1770} 1771 1772-(NPError)getURL:(const char *)URLCString target:(const char *)cTarget 1773{ 1774 LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget); 1775 1776 NSMutableURLRequest *request = [self requestWithURLCString:URLCString]; 1777 return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO]; 1778} 1779 1780- (NPError)_postURL:(const char *)URLCString 1781 target:(const char *)target 1782 len:(UInt32)len 1783 buf:(const char *)buf 1784 file:(NPBool)file 1785 notifyData:(void *)notifyData 1786 sendNotification:(BOOL)sendNotification 1787 allowHeaders:(BOOL)allowHeaders 1788{ 1789 if (!URLCString || !len || !buf) { 1790 return NPERR_INVALID_PARAM; 1791 } 1792 1793 NSData *postData = nil; 1794 1795 if (file) { 1796 // If we're posting a file, buf is either a file URL or a path to the file. 1797 NSString *bufString = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingWindowsLatin1); 1798 if (!bufString) { 1799 return NPERR_INVALID_PARAM; 1800 } 1801 NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString]; 1802 NSString *path; 1803 if ([fileURL isFileURL]) { 1804 path = [fileURL path]; 1805 } else { 1806 path = bufString; 1807 } 1808 postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]]; 1809 CFRelease(bufString); 1810 if (!postData) { 1811 return NPERR_FILE_NOT_FOUND; 1812 } 1813 } else { 1814 postData = [NSData dataWithBytes:buf length:len]; 1815 } 1816 1817 if ([postData length] == 0) { 1818 return NPERR_INVALID_PARAM; 1819 } 1820 1821 NSMutableURLRequest *request = [self requestWithURLCString:URLCString]; 1822 [request setHTTPMethod:@"POST"]; 1823 1824 if (allowHeaders) { 1825 if ([postData _web_startsWithBlankLine]) { 1826 postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)]; 1827 } else { 1828 NSInteger location = [postData _web_locationAfterFirstBlankLine]; 1829 if (location != NSNotFound) { 1830 // If the blank line is somewhere in the middle of postData, everything before is the header. 1831 NSData *headerData = [postData subdataWithRange:NSMakeRange(0, location)]; 1832 NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields]; 1833 unsigned dataLength = [postData length] - location; 1834 1835 // Sometimes plugins like to set Content-Length themselves when they post, 1836 // but WebFoundation does not like that. So we will remove the header 1837 // and instead truncate the data to the requested length. 1838 NSString *contentLength = [header objectForKey:@"Content-Length"]; 1839 1840 if (contentLength != nil) 1841 dataLength = std::min<unsigned>([contentLength intValue], dataLength); 1842 [header removeObjectForKey:@"Content-Length"]; 1843 1844 if ([header count] > 0) { 1845 [request setAllHTTPHeaderFields:header]; 1846 } 1847 // Everything after the blank line is the actual content of the POST. 1848 postData = [postData subdataWithRange:NSMakeRange(location, dataLength)]; 1849 1850 } 1851 } 1852 if ([postData length] == 0) { 1853 return NPERR_INVALID_PARAM; 1854 } 1855 } 1856 1857 // Plug-ins expect to receive uncached data when doing a POST (3347134). 1858 [request setCachePolicy:NSURLRequestReloadIgnoringCacheData]; 1859 [request setHTTPBody:postData]; 1860 1861 return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification]; 1862} 1863 1864- (NPError)postURLNotify:(const char *)URLCString 1865 target:(const char *)target 1866 len:(UInt32)len 1867 buf:(const char *)buf 1868 file:(NPBool)file 1869 notifyData:(void *)notifyData 1870{ 1871 LOG(Plugins, "NPN_PostURLNotify: %s", URLCString); 1872 return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:notifyData sendNotification:YES allowHeaders:YES]; 1873} 1874 1875-(NPError)postURL:(const char *)URLCString 1876 target:(const char *)target 1877 len:(UInt32)len 1878 buf:(const char *)buf 1879 file:(NPBool)file 1880{ 1881 LOG(Plugins, "NPN_PostURL: %s", URLCString); 1882 // As documented, only allow headers to be specified via NPP_PostURL when using a file. 1883 return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:NULL sendNotification:NO allowHeaders:file]; 1884} 1885 1886-(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream 1887{ 1888 LOG(Plugins, "NPN_NewStream"); 1889 return NPERR_GENERIC_ERROR; 1890} 1891 1892-(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer 1893{ 1894 LOG(Plugins, "NPN_Write"); 1895 return NPERR_GENERIC_ERROR; 1896} 1897 1898-(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason 1899{ 1900 LOG(Plugins, "NPN_DestroyStream"); 1901 // This function does a sanity check to ensure that the NPStream provided actually 1902 // belongs to the plug-in that provided it, which fixes a crash in the DivX 1903 // plug-in: <rdar://problem/5093862> | http://bugs.webkit.org/show_bug.cgi?id=13203 1904 if (!stream || WebNetscapePluginStream::ownerForStream(stream) != plugin) { 1905 LOG(Plugins, "Invalid NPStream passed to NPN_DestroyStream: %p", stream); 1906 return NPERR_INVALID_INSTANCE_ERROR; 1907 } 1908 1909 WebNetscapePluginStream* browserStream = static_cast<WebNetscapePluginStream*>(stream->ndata); 1910 browserStream->cancelLoadAndDestroyStreamWithError(browserStream->errorForReason(reason)); 1911 1912 return NPERR_NO_ERROR; 1913} 1914 1915- (const char *)userAgent 1916{ 1917 NSString *userAgent = [[self webView] userAgentForURL:_baseURL.get()]; 1918 1919 if (_isSilverlight) { 1920 // Silverlight has a workaround for a leak in Safari 2. This workaround is 1921 // applied when the user agent does not contain "Version/3" so we append it 1922 // at the end of the user agent. 1923 userAgent = [userAgent stringByAppendingString:@" Version/3.2.1"]; 1924 } 1925 1926 return [userAgent UTF8String]; 1927} 1928 1929-(void)status:(const char *)message 1930{ 1931 CFStringRef status = CFStringCreateWithCString(NULL, message ? message : "", kCFStringEncodingUTF8); 1932 if (!status) { 1933 LOG_ERROR("NPN_Status: the message was not valid UTF-8"); 1934 return; 1935 } 1936 1937 LOG(Plugins, "NPN_Status: %@", status); 1938 WebView *wv = [self webView]; 1939 [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status]; 1940 CFRelease(status); 1941} 1942 1943-(void)invalidateRect:(NPRect *)invalidRect 1944{ 1945 LOG(Plugins, "NPN_InvalidateRect"); 1946 [self invalidatePluginContentRect:NSMakeRect(invalidRect->left, invalidRect->top, 1947 (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)]; 1948} 1949 1950- (void)invalidateRegion:(NPRegion)invalidRegion 1951{ 1952 LOG(Plugins, "NPN_InvalidateRegion"); 1953 NSRect invalidRect = NSZeroRect; 1954 switch (drawingModel) { 1955#ifndef NP_NO_QUICKDRAW 1956 case NPDrawingModelQuickDraw: 1957 { 1958 ::Rect qdRect; 1959 GetRegionBounds((NPQDRegion)invalidRegion, &qdRect); 1960 invalidRect = NSMakeRect(qdRect.left, qdRect.top, qdRect.right - qdRect.left, qdRect.bottom - qdRect.top); 1961 } 1962 break; 1963#endif /* NP_NO_QUICKDRAW */ 1964 1965 case NPDrawingModelCoreGraphics: 1966 { 1967 CGRect cgRect = CGPathGetBoundingBox((NPCGRegion)invalidRegion); 1968 invalidRect = *(NSRect*)&cgRect; 1969 break; 1970 } 1971 default: 1972 ASSERT_NOT_REACHED(); 1973 break; 1974 } 1975 1976 [self invalidatePluginContentRect:invalidRect]; 1977} 1978 1979-(void)forceRedraw 1980{ 1981 LOG(Plugins, "forceRedraw"); 1982 [self invalidatePluginContentRect:[self bounds]]; 1983 [[self window] displayIfNeeded]; 1984} 1985 1986- (NPError)getVariable:(NPNVariable)variable value:(void *)value 1987{ 1988 switch (static_cast<unsigned>(variable)) { 1989 case NPNVWindowNPObject: 1990 { 1991 Frame* frame = core([self webFrame]); 1992 NPObject* windowScriptObject = frame ? frame->script().windowScriptNPObject() : 0; 1993 1994 // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess> 1995 if (windowScriptObject) 1996 _NPN_RetainObject(windowScriptObject); 1997 1998 void **v = (void **)value; 1999 *v = windowScriptObject; 2000 2001 return NPERR_NO_ERROR; 2002 } 2003 2004 case NPNVPluginElementNPObject: 2005 { 2006 if (!_element) 2007 return NPERR_GENERIC_ERROR; 2008 2009 NPObject *plugInScriptObject = _element->getNPObject(); 2010 2011 // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess> 2012 if (plugInScriptObject) 2013 _NPN_RetainObject(plugInScriptObject); 2014 2015 void **v = (void **)value; 2016 *v = plugInScriptObject; 2017 2018 return NPERR_NO_ERROR; 2019 } 2020 2021 case NPNVpluginDrawingModel: 2022 { 2023 *(NPDrawingModel *)value = drawingModel; 2024 return NPERR_NO_ERROR; 2025 } 2026 2027#ifndef NP_NO_QUICKDRAW 2028 case NPNVsupportsQuickDrawBool: 2029 { 2030 *(NPBool *)value = TRUE; 2031 return NPERR_NO_ERROR; 2032 } 2033#endif /* NP_NO_QUICKDRAW */ 2034 2035 case NPNVsupportsCoreGraphicsBool: 2036 { 2037 *(NPBool *)value = TRUE; 2038 return NPERR_NO_ERROR; 2039 } 2040 2041 case NPNVsupportsOpenGLBool: 2042 { 2043 *(NPBool *)value = FALSE; 2044 return NPERR_NO_ERROR; 2045 } 2046 2047 case NPNVsupportsCoreAnimationBool: 2048 { 2049 *(NPBool *)value = TRUE; 2050 return NPERR_NO_ERROR; 2051 } 2052 2053#ifndef NP_NO_CARBON 2054 case NPNVsupportsCarbonBool: 2055 { 2056 *(NPBool *)value = TRUE; 2057 return NPERR_NO_ERROR; 2058 } 2059#endif /* NP_NO_CARBON */ 2060 2061 case NPNVsupportsCocoaBool: 2062 { 2063 *(NPBool *)value = TRUE; 2064 return NPERR_NO_ERROR; 2065 } 2066 2067 case NPNVprivateModeBool: 2068 { 2069 *(NPBool *)value = _isPrivateBrowsingEnabled; 2070 return NPERR_NO_ERROR; 2071 } 2072 2073 case WKNVBrowserContainerCheckFuncs: 2074 { 2075 *(WKNBrowserContainerCheckFuncs **)value = browserContainerCheckFuncs(); 2076 return NPERR_NO_ERROR; 2077 } 2078 2079 case WKNVSupportsCompositingCoreAnimationPluginsBool: 2080 { 2081 *(NPBool *)value = [[[self webView] preferences] acceleratedCompositingEnabled]; 2082 return NPERR_NO_ERROR; 2083 } 2084 2085 default: 2086 break; 2087 } 2088 2089 return NPERR_GENERIC_ERROR; 2090} 2091 2092- (NPError)setVariable:(NPPVariable)variable value:(void *)value 2093{ 2094 switch (variable) { 2095 case NPPVpluginDrawingModel: 2096 { 2097 // Can only set drawing model inside NPP_New() 2098 if (self != [[self class] currentPluginView]) 2099 return NPERR_GENERIC_ERROR; 2100 2101 // Check for valid, supported drawing model 2102 NPDrawingModel newDrawingModel = (NPDrawingModel)(uintptr_t)value; 2103 switch (newDrawingModel) { 2104 // Supported drawing models: 2105#ifndef NP_NO_QUICKDRAW 2106 case NPDrawingModelQuickDraw: 2107#endif 2108 case NPDrawingModelCoreGraphics: 2109 case NPDrawingModelCoreAnimation: 2110 drawingModel = newDrawingModel; 2111 return NPERR_NO_ERROR; 2112 2113 2114 // Unsupported (or unknown) drawing models: 2115 default: 2116 LOG(Plugins, "Plugin %@ uses unsupported drawing model: %d", _eventHandler.get(), drawingModel); 2117 return NPERR_GENERIC_ERROR; 2118 } 2119 } 2120 2121 case NPPVpluginEventModel: 2122 { 2123 // Can only set event model inside NPP_New() 2124 if (self != [[self class] currentPluginView]) 2125 return NPERR_GENERIC_ERROR; 2126 2127 // Check for valid, supported event model 2128 NPEventModel newEventModel = (NPEventModel)(uintptr_t)value; 2129 switch (newEventModel) { 2130 // Supported event models: 2131#ifndef NP_NO_CARBON 2132 case NPEventModelCarbon: 2133#endif 2134 case NPEventModelCocoa: 2135 eventModel = newEventModel; 2136 return NPERR_NO_ERROR; 2137 2138 // Unsupported (or unknown) event models: 2139 default: 2140 LOG(Plugins, "Plugin %@ uses unsupported event model: %d", _eventHandler.get(), eventModel); 2141 return NPERR_GENERIC_ERROR; 2142 } 2143 } 2144 2145 default: 2146 return NPERR_GENERIC_ERROR; 2147 } 2148} 2149 2150- (uint32_t)scheduleTimerWithInterval:(uint32_t)interval repeat:(NPBool)repeat timerFunc:(void (*)(NPP npp, uint32_t timerID))timerFunc 2151{ 2152 if (!timerFunc) 2153 return 0; 2154 2155 if (!timers) 2156 timers = std::make_unique<HashMap<uint32_t, std::unique_ptr<PluginTimer>>>(); 2157 2158 std::unique_ptr<PluginTimer>* slot; 2159 uint32_t timerID; 2160 do 2161 timerID = ++currentTimerID; 2162 while (!timers->isValidKey(timerID) || *(slot = &timers->add(timerID, nullptr).iterator->value)); 2163 2164 auto timer = std::make_unique<PluginTimer>(plugin, timerID, interval, repeat, timerFunc); 2165 2166 if (_shouldFireTimers) 2167 timer->start(_isCompletelyObscured); 2168 2169 *slot = WTF::move(timer); 2170 2171 return timerID; 2172} 2173 2174- (void)unscheduleTimer:(uint32_t)timerID 2175{ 2176 if (!timers) 2177 return; 2178 2179 timers->remove(timerID); 2180} 2181 2182- (NPError)popUpContextMenu:(NPMenu *)menu 2183{ 2184 NSEvent *currentEvent = [NSApp currentEvent]; 2185 2186 // NPN_PopUpContextMenu must be called from within the plug-in's NPP_HandleEvent. 2187 if (!currentEvent) 2188 return NPERR_GENERIC_ERROR; 2189 2190 [NSMenu popUpContextMenu:(NSMenu *)menu withEvent:currentEvent forView:self]; 2191 return NPERR_NO_ERROR; 2192} 2193 2194- (NPError)getVariable:(NPNURLVariable)variable forURL:(const char*)url value:(char**)value length:(uint32_t*)length 2195{ 2196 switch (variable) { 2197 case NPNURLVCookie: { 2198 if (!value) 2199 break; 2200 2201 NSURL *URL = [self URLWithCString:url]; 2202 if (!URL) 2203 break; 2204 2205 if (Frame* frame = core([self webFrame])) { 2206 String cookieString = cookies(frame->document(), URL); 2207 CString cookieStringUTF8 = cookieString.utf8(); 2208 if (cookieStringUTF8.isNull()) 2209 return NPERR_GENERIC_ERROR; 2210 2211 *value = static_cast<char*>(NPN_MemAlloc(cookieStringUTF8.length())); 2212 memcpy(*value, cookieStringUTF8.data(), cookieStringUTF8.length()); 2213 2214 if (length) 2215 *length = cookieStringUTF8.length(); 2216 return NPERR_NO_ERROR; 2217 } 2218 break; 2219 } 2220 case NPNURLVProxy: { 2221 if (!value) 2222 break; 2223 2224 NSURL *URL = [self URLWithCString:url]; 2225 if (!URL) 2226 break; 2227 2228 Vector<ProxyServer> proxyServers = proxyServersForURL(URL, 0); 2229 CString proxiesUTF8 = toString(proxyServers).utf8(); 2230 2231 *value = static_cast<char*>(NPN_MemAlloc(proxiesUTF8.length())); 2232 memcpy(*value, proxiesUTF8.data(), proxiesUTF8.length()); 2233 2234 if (length) 2235 *length = proxiesUTF8.length(); 2236 2237 return NPERR_NO_ERROR; 2238 } 2239 } 2240 return NPERR_GENERIC_ERROR; 2241} 2242 2243- (NPError)setVariable:(NPNURLVariable)variable forURL:(const char*)url value:(const char*)value length:(uint32_t)length 2244{ 2245 switch (variable) { 2246 case NPNURLVCookie: { 2247 NSURL *URL = [self URLWithCString:url]; 2248 if (!URL) 2249 break; 2250 2251 String cookieString = String::fromUTF8(value, length); 2252 if (!cookieString) 2253 break; 2254 2255 if (Frame* frame = core([self webFrame])) { 2256 setCookies(frame->document(), URL, cookieString); 2257 return NPERR_NO_ERROR; 2258 } 2259 2260 break; 2261 } 2262 case NPNURLVProxy: 2263 // Can't set the proxy for a URL. 2264 break; 2265 } 2266 return NPERR_GENERIC_ERROR; 2267} 2268 2269- (NPError)getAuthenticationInfoWithProtocol:(const char*)protocolStr host:(const char*)hostStr port:(int32_t)port scheme:(const char*)schemeStr realm:(const char*)realmStr 2270 username:(char**)usernameStr usernameLength:(uint32_t*)usernameLength 2271 password:(char**)passwordStr passwordLength:(uint32_t*)passwordLength 2272{ 2273 if (!protocolStr || !hostStr || !schemeStr || !realmStr || !usernameStr || !usernameLength || !passwordStr || !passwordLength) 2274 return NPERR_GENERIC_ERROR; 2275 2276 CString username; 2277 CString password; 2278 if (!getAuthenticationInfo(protocolStr, hostStr, port, schemeStr, realmStr, username, password)) 2279 return NPERR_GENERIC_ERROR; 2280 2281 *usernameLength = username.length(); 2282 *usernameStr = static_cast<char*>(NPN_MemAlloc(username.length())); 2283 memcpy(*usernameStr, username.data(), username.length()); 2284 2285 *passwordLength = password.length(); 2286 *passwordStr = static_cast<char*>(NPN_MemAlloc(password.length())); 2287 memcpy(*passwordStr, password.data(), password.length()); 2288 2289 return NPERR_NO_ERROR; 2290} 2291 2292- (char*)resolveURL:(const char*)url forTarget:(const char*)target 2293{ 2294 CString location = [self resolvedURLStringForURL:url target:target]; 2295 2296 if (location.isNull()) 2297 return 0; 2298 2299 // We use strdup here because the caller needs to free it with NPN_MemFree (which calls free). 2300 return strdup(location.data()); 2301} 2302 2303@end 2304 2305@implementation WebNetscapePluginView (Internal) 2306 2307- (BOOL)_shouldCancelSrcStream 2308{ 2309 ASSERT(_isStarted); 2310 2311 // Check if we should cancel the load 2312 NPBool cancelSrcStream = 0; 2313 if ([_pluginPackage.get() pluginFuncs]->getvalue && 2314 [_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginCancelSrcStream, &cancelSrcStream) == NPERR_NO_ERROR && cancelSrcStream) 2315 return YES; 2316 2317 return NO; 2318} 2319 2320// Work around Silverlight full screen performance issue by maintaining an accelerated GL pixel format. 2321// We can safely remove it at some point in the future when both: 2322// 1) Microsoft releases a genuine fix for 7288546. 2323// 2) Enough Silverlight users update to the new Silverlight. 2324// For now, we'll distinguish older broken versions of Silverlight by asking the plug-in if it resolved its full screen badness. 2325- (void)_workaroundSilverlightFullscreenBug:(BOOL)initializedPlugin 2326{ 2327 ASSERT(_isSilverlight); 2328 NPBool isFullscreenPerformanceIssueFixed = 0; 2329 NPPluginFuncs *pluginFuncs = [_pluginPackage.get() pluginFuncs]; 2330 if (pluginFuncs->getvalue && pluginFuncs->getvalue(plugin, static_cast<NPPVariable>(WKNVSilverlightFullscreenPerformanceIssueFixed), &isFullscreenPerformanceIssueFixed) == NPERR_NO_ERROR && isFullscreenPerformanceIssueFixed) 2331 return; 2332 2333 static CGLPixelFormatObj pixelFormatObject = 0; 2334 static unsigned refCount = 0; 2335 2336 if (initializedPlugin) { 2337 refCount++; 2338 if (refCount == 1) { 2339 const CGLPixelFormatAttribute attributes[] = { kCGLPFAAccelerated, static_cast<CGLPixelFormatAttribute>(0) }; 2340 GLint npix; 2341 CGLChoosePixelFormat(attributes, &pixelFormatObject, &npix); 2342 } 2343 } else { 2344 ASSERT(pixelFormatObject); 2345 refCount--; 2346 if (!refCount) 2347 CGLReleasePixelFormat(pixelFormatObject); 2348 } 2349} 2350 2351- (NPError)_createPlugin 2352{ 2353 plugin = (NPP)calloc(1, sizeof(NPP_t)); 2354 plugin->ndata = self; 2355 2356 ASSERT([_pluginPackage.get() pluginFuncs]->newp); 2357 2358 // NPN_New(), which creates the plug-in instance, should never be called while calling a plug-in function for that instance. 2359 ASSERT(pluginFunctionCallDepth == 0); 2360 2361 PluginMainThreadScheduler::scheduler().registerPlugin(plugin); 2362 2363 _isFlash = [_pluginPackage.get() bundleIdentifier] == "com.macromedia.Flash Player.plugin"; 2364 _isSilverlight = [_pluginPackage.get() bundleIdentifier] == "com.microsoft.SilverlightPlugin"; 2365 2366 [[self class] setCurrentPluginView:self]; 2367 NPError npErr = [_pluginPackage.get() pluginFuncs]->newp((char *)[_MIMEType.get() cString], plugin, _mode, argsCount, cAttributes, cValues, NULL); 2368 [[self class] setCurrentPluginView:nil]; 2369 if (_isSilverlight) 2370 [self _workaroundSilverlightFullscreenBug:YES]; 2371 LOG(Plugins, "NPP_New: %d", npErr); 2372 return npErr; 2373} 2374 2375- (void)_destroyPlugin 2376{ 2377 PluginMainThreadScheduler::scheduler().unregisterPlugin(plugin); 2378 2379 if (_isSilverlight) 2380 [self _workaroundSilverlightFullscreenBug:NO]; 2381 2382 NPError npErr; 2383 npErr = ![_pluginPackage.get() pluginFuncs]->destroy(plugin, NULL); 2384 LOG(Plugins, "NPP_Destroy: %d", npErr); 2385 2386 if (Frame* frame = core([self webFrame])) 2387 frame->script().cleanupScriptObjectsForPlugin(self); 2388 2389 free(plugin); 2390 plugin = NULL; 2391} 2392 2393- (NSBitmapImageRep *)_printedPluginBitmap 2394{ 2395#ifdef NP_NO_QUICKDRAW 2396 return nil; 2397#else 2398 // Cannot print plugins that do not implement NPP_Print 2399 if (![_pluginPackage.get() pluginFuncs]->print) 2400 return nil; 2401 2402 // This NSBitmapImageRep will share its bitmap buffer with a GWorld that the plugin will draw into. 2403 // The bitmap is created in 32-bits-per-pixel ARGB format, which is the default GWorld pixel format. 2404 NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL 2405 pixelsWide:window.width 2406 pixelsHigh:window.height 2407 bitsPerSample:8 2408 samplesPerPixel:4 2409 hasAlpha:YES 2410 isPlanar:NO 2411 colorSpaceName:NSDeviceRGBColorSpace 2412 bitmapFormat:NSAlphaFirstBitmapFormat 2413 bytesPerRow:0 2414 bitsPerPixel:0] autorelease]; 2415 ASSERT(bitmap); 2416 2417 // Create a GWorld with the same underlying buffer into which the plugin can draw 2418 ::Rect printGWorldBounds; 2419 SetRect(&printGWorldBounds, 0, 0, window.width, window.height); 2420 GWorldPtr printGWorld; 2421 if (NewGWorldFromPtr(&printGWorld, 2422 k32ARGBPixelFormat, 2423 &printGWorldBounds, 2424 NULL, 2425 NULL, 2426 0, 2427 (Ptr)[bitmap bitmapData], 2428 [bitmap bytesPerRow]) != noErr) { 2429 LOG_ERROR("Could not create GWorld for printing"); 2430 return nil; 2431 } 2432 2433 /// Create NPWindow for the GWorld 2434 NPWindow printNPWindow; 2435 printNPWindow.window = &printGWorld; // Normally this is an NP_Port, but when printing it is the actual CGrafPtr 2436 printNPWindow.x = 0; 2437 printNPWindow.y = 0; 2438 printNPWindow.width = window.width; 2439 printNPWindow.height = window.height; 2440 printNPWindow.clipRect.top = 0; 2441 printNPWindow.clipRect.left = 0; 2442 printNPWindow.clipRect.right = window.width; 2443 printNPWindow.clipRect.bottom = window.height; 2444 printNPWindow.type = NPWindowTypeDrawable; // Offscreen graphics port as opposed to a proper window 2445 2446 // Create embed-mode NPPrint 2447 NPPrint npPrint; 2448 npPrint.mode = NP_EMBED; 2449 npPrint.print.embedPrint.window = printNPWindow; 2450 npPrint.print.embedPrint.platformPrint = printGWorld; 2451 2452 // Tell the plugin to print into the GWorld 2453 [self willCallPlugInFunction]; 2454 { 2455 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 2456 [_pluginPackage.get() pluginFuncs]->print(plugin, &npPrint); 2457 } 2458 [self didCallPlugInFunction]; 2459 2460 // Don't need the GWorld anymore 2461 DisposeGWorld(printGWorld); 2462 2463 return bitmap; 2464#endif 2465} 2466 2467- (void)_redeliverStream 2468{ 2469 if ([self dataSource] && _isStarted) { 2470 // Deliver what has not been passed to the plug-in up to this point. 2471 if (_dataLengthReceived > 0) { 2472 NSData *data = [[[self dataSource] data] subdataWithRange:NSMakeRange(0, _dataLengthReceived)]; 2473 _dataLengthReceived = 0; 2474 [self pluginView:self receivedData:data]; 2475 if (![[self dataSource] isLoading]) { 2476 if (_error) 2477 [self pluginView:self receivedError:_error.get()]; 2478 else 2479 [self pluginViewFinishedLoading:self]; 2480 } 2481 } 2482 } 2483} 2484 2485@end 2486 2487#endif 2488