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