2008-11-04 Anders Carlsson <andersca@apple.com>
[webkit/qt.git] / WebKit / mac / Plugins / WebBaseNetscapePluginView.mm
blob769d65897ac9749f15617fef6a0a4a38f8574ccd
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  */
29 #if ENABLE(NETSCAPE_PLUGIN_API)
31 #import "WebBaseNetscapePluginView.h"
33 #import "WebDataSourceInternal.h"
34 #import "WebDefaultUIDelegate.h"
35 #import "WebFrameInternal.h" 
36 #import "WebFrameView.h"
37 #import "WebGraphicsExtras.h"
38 #import "WebKitLogging.h"
39 #import "WebKitNSStringExtras.h"
40 #import "WebKitSystemInterface.h"
41 #import "WebNSDataExtras.h"
42 #import "WebNSDictionaryExtras.h"
43 #import "WebNSObjectExtras.h"
44 #import "WebNSURLExtras.h"
45 #import "WebNSURLRequestExtras.h"
46 #import "WebNSViewExtras.h"
47 #import "WebNetscapePluginPackage.h"
48 #import "WebBaseNetscapePluginStream.h"
49 #import "WebNetscapePluginEventHandler.h"
50 #import "WebNullPluginView.h"
51 #import "WebPreferences.h"
52 #import "WebViewInternal.h"
53 #import "WebUIDelegatePrivate.h"
54 #import <Carbon/Carbon.h>
55 #import <runtime/JSLock.h>
56 #import <WebCore/npruntime_impl.h>
57 #import <WebCore/Document.h>
58 #import <WebCore/DocumentLoader.h>
59 #import <WebCore/Element.h>
60 #import <WebCore/Frame.h> 
61 #import <WebCore/FrameLoader.h> 
62 #import <WebCore/FrameTree.h> 
63 #import <WebCore/Page.h> 
64 #import <WebCore/PluginMainThreadScheduler.h>
65 #import <WebCore/ScriptController.h>
66 #import <WebCore/SoftLinking.h> 
67 #import <WebCore/WebCoreObjCExtras.h>
68 #import <WebKit/nptextinput.h>
69 #import <WebKit/DOMPrivate.h>
70 #import <WebKit/WebUIDelegate.h>
71 #import <wtf/Assertions.h>
72 #import <objc/objc-runtime.h>
74 using namespace WebCore;
76 #define LoginWindowDidSwitchFromUserNotification    @"WebLoginWindowDidSwitchFromUserNotification"
77 #define LoginWindowDidSwitchToUserNotification      @"WebLoginWindowDidSwitchToUserNotification"
79 static inline bool isDrawingModelQuickDraw(NPDrawingModel drawingModel)
81 #ifndef NP_NO_QUICKDRAW
82     return drawingModel == NPDrawingModelQuickDraw;
83 #else
84     return false;
85 #endif
88 @interface WebBaseNetscapePluginView (Internal)
89 - (void)_viewHasMoved;
90 - (NPError)_createPlugin;
91 - (void)_destroyPlugin;
92 - (NSBitmapImageRep *)_printedPluginBitmap;
93 - (void)_redeliverStream;
94 @end
96 static WebBaseNetscapePluginView *currentPluginView = nil;
98 typedef struct OpaquePortState* PortState;
100 static const double ThrottledTimerInterval = 0.25;
102 class PluginTimer : public TimerBase {
103 public:
104     typedef void (*TimerFunc)(NPP npp, uint32 timerID);
105     
106     PluginTimer(NPP npp, uint32 timerID, uint32 interval, NPBool repeat, TimerFunc timerFunc)
107         : m_npp(npp)
108         , m_timerID(timerID)
109         , m_interval(interval)
110         , m_repeat(repeat)
111         , m_timerFunc(timerFunc)
112     {
113     }
114     
115     void start(bool throttle)
116     {
117         ASSERT(!isActive());
118         
119         double timeInterval = throttle ? ThrottledTimerInterval : m_interval / 1000.0;
120         if (m_repeat)
121             startRepeating(timeInterval);
122         else
123             startOneShot(timeInterval);
124     }
126 private:
127     virtual void fired() 
128     {
129         m_timerFunc(m_npp, m_timerID);
130         if (!m_repeat)
131             delete this;
132     }
133     
134     NPP m_npp;
135     uint32 m_timerID;
136     uint32 m_interval;
137     NPBool m_repeat;
138     TimerFunc m_timerFunc;
141 #ifndef NP_NO_QUICKDRAW
143 // QuickDraw is not available in 64-bit
145 typedef struct {
146     GrafPtr oldPort;
147     GDHandle oldDevice;
148     Point oldOrigin;
149     RgnHandle oldClipRegion;
150     RgnHandle oldVisibleRegion;
151     RgnHandle clipRegion;
152     BOOL forUpdate;
153 } PortState_QD;
155 #endif /* NP_NO_QUICKDRAW */
157 typedef struct {
158     CGContextRef context;
159 } PortState_CG;
161 @class NSTextInputContext;
162 @interface NSResponder (AppKitDetails)
163 - (NSTextInputContext *)inputContext;
164 @end
166 @interface WebPluginRequest : NSObject
168     NSURLRequest *_request;
169     NSString *_frameName;
170     void *_notifyData;
171     BOOL _didStartFromUserGesture;
172     BOOL _sendNotification;
175 - (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture;
177 - (NSURLRequest *)request;
178 - (NSString *)frameName;
179 - (void *)notifyData;
180 - (BOOL)isCurrentEventUserGesture;
181 - (BOOL)sendNotification;
183 @end
185 @interface NSData (WebPluginDataExtras)
186 - (BOOL)_web_startsWithBlankLine;
187 - (NSInteger)_web_locationAfterFirstBlankLine;
188 @end
190 @interface WebBaseNetscapePluginView (ForwardDeclarations)
191 - (void)setWindowIfNecessary;
192 - (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification;
193 @end
195 @implementation WebBaseNetscapePluginView
197 + (void)initialize
199 #ifndef BUILDING_ON_TIGER
200     WebCoreObjCFinalizeOnMainThread(self);
201 #endif
202     WKSendUserChangeNotifications();
205 #pragma mark EVENTS
207 - (BOOL)superviewsHaveSuperviews
209     NSView *contentView = [[self window] contentView];
210     NSView *view;
211     for (view = self; view != nil; view = [view superview]) { 
212         if (view == contentView) {
213             return YES;
214         }
215     }
216     return NO;
220 // The WindowRef created by -[NSWindow windowRef] has a QuickDraw GrafPort that covers 
221 // the entire window frame (or structure region to use the Carbon term) rather then just the window content.
222 // We can remove this when <rdar://problem/4201099> is fixed.
223 - (void)fixWindowPort
225 #ifndef NP_NO_QUICKDRAW
226     ASSERT(isDrawingModelQuickDraw(drawingModel));
227     
228     NSWindow *currentWindow = [self currentWindow];
229     if ([currentWindow isKindOfClass:objc_getClass("NSCarbonWindow")])
230         return;
231     
232     float windowHeight = [currentWindow frame].size.height;
233     NSView *contentView = [currentWindow contentView];
234     NSRect contentRect = [contentView convertRect:[contentView frame] toView:nil]; // convert to window-relative coordinates
235     
236     CGrafPtr oldPort;
237     GetPort(&oldPort);    
238     SetPort(GetWindowPort((WindowRef)[currentWindow windowRef]));
239     
240     MovePortTo(static_cast<short>(contentRect.origin.x), /* Flip Y */ static_cast<short>(windowHeight - NSMaxY(contentRect)));
241     PortSize(static_cast<short>(contentRect.size.width), static_cast<short>(contentRect.size.height));
242     
243     SetPort(oldPort);
244 #endif
247 #ifndef NP_NO_QUICKDRAW
248 static UInt32 getQDPixelFormatForBitmapContext(CGContextRef context)
250     UInt32 byteOrder = CGBitmapContextGetBitmapInfo(context) & kCGBitmapByteOrderMask;
251     if (byteOrder == kCGBitmapByteOrderDefault)
252         switch (CGBitmapContextGetBitsPerPixel(context)) {
253             case 16:
254                 byteOrder = kCGBitmapByteOrder16Host;
255                 break;
256             case 32:
257                 byteOrder = kCGBitmapByteOrder32Host;
258                 break;
259         }
260     switch (byteOrder) {
261         case kCGBitmapByteOrder16Little:
262             return k16LE555PixelFormat;
263         case kCGBitmapByteOrder32Little:
264             return k32BGRAPixelFormat;
265         case kCGBitmapByteOrder16Big:
266             return k16BE555PixelFormat;
267         case kCGBitmapByteOrder32Big:
268             return k32ARGBPixelFormat;
269     }
270     ASSERT_NOT_REACHED();
271     return 0;
274 static inline void getNPRect(const CGRect& cgr, NPRect& npr)
276     npr.top = static_cast<uint16>(cgr.origin.y);
277     npr.left = static_cast<uint16>(cgr.origin.x);
278     npr.bottom = static_cast<uint16>(CGRectGetMaxY(cgr));
279     npr.right = static_cast<uint16>(CGRectGetMaxX(cgr));
282 #endif
284 static inline void getNPRect(const NSRect& nr, NPRect& npr)
286     npr.top = static_cast<uint16>(nr.origin.y);
287     npr.left = static_cast<uint16>(nr.origin.x);
288     npr.bottom = static_cast<uint16>(NSMaxY(nr));
289     npr.right = static_cast<uint16>(NSMaxX(nr));
292 - (NSRect)visibleRect
294     // WebCore may impose an additional clip (via CSS overflow or clip properties).  Fetch
295     // that clip now.    
296     return NSIntersectionRect([self convertRect:[_element.get() _windowClipRect] fromView:nil], [super visibleRect]);
299 - (PortState)saveAndSetNewPortStateForUpdate:(BOOL)forUpdate
301     ASSERT(drawingModel != NPDrawingModelCoreAnimation);
302     ASSERT([self currentWindow] != nil);
304     // Use AppKit to convert view coordinates to NSWindow coordinates.
305     NSRect boundsInWindow = [self convertRect:[self bounds] toView:nil];
306     NSRect visibleRectInWindow = [self convertRect:[self visibleRect] toView:nil];
307     
308     // Flip Y to convert NSWindow coordinates to top-left-based window coordinates.
309     float borderViewHeight = [[self currentWindow] frame].size.height;
310     boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow);
311     visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow);
312     
313 #ifndef NP_NO_QUICKDRAW
314     WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
315     ASSERT(windowRef);
317     // Look at the Carbon port to convert top-left-based window coordinates into top-left-based content coordinates.
318     if (isDrawingModelQuickDraw(drawingModel)) {
319         // If drawing with QuickDraw, fix the window port so that it has the same bounds as the NSWindow's
320         // content view.  This makes it easier to convert between AppKit view and QuickDraw port coordinates.
321         [self fixWindowPort];
322         
323         ::Rect portBounds;
324         CGrafPtr port = GetWindowPort(windowRef);
325         GetPortBounds(port, &portBounds);
327         PixMap *pix = *GetPortPixMap(port);
328         boundsInWindow.origin.x += pix->bounds.left - portBounds.left;
329         boundsInWindow.origin.y += pix->bounds.top - portBounds.top;
330         visibleRectInWindow.origin.x += pix->bounds.left - portBounds.left;
331         visibleRectInWindow.origin.y += pix->bounds.top - portBounds.top;
332     }
333 #endif
334     
335     window.type = NPWindowTypeWindow;
336     window.x = (int32)boundsInWindow.origin.x; 
337     window.y = (int32)boundsInWindow.origin.y;
338     window.width = static_cast<uint32>(NSWidth(boundsInWindow));
339     window.height = static_cast<uint32>(NSHeight(boundsInWindow));
340     
341     // "Clip-out" the plug-in when:
342     // 1) it's not really in a window or off-screen or has no height or width.
343     // 2) window.x is a "big negative number" which is how WebCore expresses off-screen widgets.
344     // 3) the window is miniaturized or the app is hidden
345     // 4) we're inside of viewWillMoveToWindow: with a nil window. In this case, superviews may already have nil 
346     // superviews and nil windows and results from convertRect:toView: are incorrect.
347     NSWindow *realWindow = [self window];
348     if (window.width <= 0 || window.height <= 0 || window.x < -100000
349             || realWindow == nil || [realWindow isMiniaturized]
350             || [NSApp isHidden]
351             || ![self superviewsHaveSuperviews]
352             || [self isHiddenOrHasHiddenAncestor]) {
354         // The following code tries to give plug-ins the same size they will eventually have.
355         // The specifiedWidth and specifiedHeight variables are used to predict the size that
356         // WebCore will eventually resize us to.
358         // The QuickTime plug-in has problems if you give it a width or height of 0.
359         // Since other plug-ins also might have the same sort of trouble, we make sure
360         // to always give plug-ins a size other than 0,0.
362         if (window.width <= 0)
363             window.width = specifiedWidth > 0 ? specifiedWidth : 100;
364         if (window.height <= 0)
365             window.height = specifiedHeight > 0 ? specifiedHeight : 100;
367         window.clipRect.bottom = window.clipRect.top;
368         window.clipRect.left = window.clipRect.right;
369     } else {
370         getNPRect(visibleRectInWindow, window.clipRect);
371     }
372     
373     // Save the port state, set up the port for entry into the plugin
374     PortState portState;
375     switch (drawingModel) {
376 #ifndef NP_NO_QUICKDRAW
377         case NPDrawingModelQuickDraw: {
378             // Set up NS_Port.
379             ::Rect portBounds;
380             CGrafPtr port = GetWindowPort(windowRef);
381             GetPortBounds(port, &portBounds);
382             nPort.qdPort.port = port;
383             nPort.qdPort.portx = (int32)-boundsInWindow.origin.x;
384             nPort.qdPort.porty = (int32)-boundsInWindow.origin.y;
385             window.window = &nPort;
387             PortState_QD *qdPortState = (PortState_QD*)malloc(sizeof(PortState_QD));
388             portState = (PortState)qdPortState;
389             
390             GetGWorld(&qdPortState->oldPort, &qdPortState->oldDevice);    
392             qdPortState->oldOrigin.h = portBounds.left;
393             qdPortState->oldOrigin.v = portBounds.top;
395             qdPortState->oldClipRegion = NewRgn();
396             GetPortClipRegion(port, qdPortState->oldClipRegion);
397             
398             qdPortState->oldVisibleRegion = NewRgn();
399             GetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
400             
401             RgnHandle clipRegion = NewRgn();
402             qdPortState->clipRegion = clipRegion;
404             CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
405             if (currentContext && WKCGContextIsBitmapContext(currentContext)) {
406                 // We use WKCGContextIsBitmapContext here, because if we just called CGBitmapContextGetData
407                 // on any context, we'd log to the console every time. But even if WKCGContextIsBitmapContext
408                 // returns true, it still might not be a context we need to create a GWorld for; for example
409                 // transparency layers will return true, but return 0 for CGBitmapContextGetData.
410                 void* offscreenData = CGBitmapContextGetData(currentContext);
411                 if (offscreenData) {
412                     // If the current context is an offscreen bitmap, then create a GWorld for it.
413                     ::Rect offscreenBounds;
414                     offscreenBounds.top = 0;
415                     offscreenBounds.left = 0;
416                     offscreenBounds.right = CGBitmapContextGetWidth(currentContext);
417                     offscreenBounds.bottom = CGBitmapContextGetHeight(currentContext);
418                     GWorldPtr newOffscreenGWorld;
419                     QDErr err = NewGWorldFromPtr(&newOffscreenGWorld,
420                         getQDPixelFormatForBitmapContext(currentContext), &offscreenBounds, 0, 0, 0,
421                         static_cast<char*>(offscreenData), CGBitmapContextGetBytesPerRow(currentContext));
422                     ASSERT(newOffscreenGWorld && !err);
423                     if (!err) {
424                         if (offscreenGWorld)
425                             DisposeGWorld(offscreenGWorld);
426                         offscreenGWorld = newOffscreenGWorld;
428                         SetGWorld(offscreenGWorld, NULL);
430                         port = offscreenGWorld;
432                         nPort.qdPort.port = port;
433                         boundsInWindow = [self bounds];
434                         
435                         // Generate a QD origin based on the current affine transform for currentContext.
436                         CGAffineTransform offscreenMatrix = CGContextGetCTM(currentContext);
437                         CGPoint origin = {0,0};
438                         CGPoint axisFlip = {1,1};
439                         origin = CGPointApplyAffineTransform(origin, offscreenMatrix);
440                         axisFlip = CGPointApplyAffineTransform(axisFlip, offscreenMatrix);
441                         
442                         // Quartz bitmaps have origins at the bottom left, but the axes may be inverted, so handle that.
443                         origin.x = offscreenBounds.left - origin.x * (axisFlip.x - origin.x);
444                         origin.y = offscreenBounds.bottom + origin.y * (axisFlip.y - origin.y);
445                         
446                         nPort.qdPort.portx = static_cast<int32>(-boundsInWindow.origin.x + origin.x);
447                         nPort.qdPort.porty = static_cast<int32>(-boundsInWindow.origin.y - origin.y);
448                         window.x = 0;
449                         window.y = 0;
450                         window.window = &nPort;
452                         // Use the clip bounds from the context instead of the bounds we created
453                         // from the window above.
454                         getNPRect(CGRectOffset(CGContextGetClipBoundingBox(currentContext), -origin.x, origin.y), window.clipRect);
455                     }
456                 }
457             }
459             MacSetRectRgn(clipRegion,
460                 window.clipRect.left + nPort.qdPort.portx, window.clipRect.top + nPort.qdPort.porty,
461                 window.clipRect.right + nPort.qdPort.portx, window.clipRect.bottom + nPort.qdPort.porty);
462             
463             // Clip to the dirty region if drawing to a window. When drawing to another bitmap context, do not clip.
464             if ([NSGraphicsContext currentContext] == [[self currentWindow] graphicsContext]) {
465                 // Clip to dirty region so plug-in does not draw over already-drawn regions of the window that are
466                 // not going to be redrawn this update.  This forces plug-ins to play nice with z-index ordering.
467                 if (forUpdate) {
468                     RgnHandle viewClipRegion = NewRgn();
469                     
470                     // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
471                     // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
472                     // knows about the true set of dirty rects.
473                     NSView *opaqueAncestor = [self opaqueAncestor];
474                     const NSRect *dirtyRects;
475                     NSInteger dirtyRectCount, dirtyRectIndex;
476                     [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount];
478                     for (dirtyRectIndex = 0; dirtyRectIndex < dirtyRectCount; dirtyRectIndex++) {
479                         NSRect dirtyRect = [self convertRect:dirtyRects[dirtyRectIndex] fromView:opaqueAncestor];
480                         if (!NSEqualSizes(dirtyRect.size, NSZeroSize)) {
481                             // Create a region for this dirty rect
482                             RgnHandle dirtyRectRegion = NewRgn();
483                             SetRectRgn(dirtyRectRegion, static_cast<short>(NSMinX(dirtyRect)), static_cast<short>(NSMinY(dirtyRect)), static_cast<short>(NSMaxX(dirtyRect)), static_cast<short>(NSMaxY(dirtyRect)));
484                             
485                             // Union this dirty rect with the rest of the dirty rects
486                             UnionRgn(viewClipRegion, dirtyRectRegion, viewClipRegion);
487                             DisposeRgn(dirtyRectRegion);
488                         }
489                     }
490                 
491                     // Intersect the dirty region with the clip region, so that we only draw over dirty parts
492                     SectRgn(clipRegion, viewClipRegion, clipRegion);
493                     DisposeRgn(viewClipRegion);
494                 }
495             }
497             // Switch to the port and set it up.
498             SetPort(port);
499             PenNormal();
500             ForeColor(blackColor);
501             BackColor(whiteColor);
502             SetOrigin(nPort.qdPort.portx, nPort.qdPort.porty);
503             SetPortClipRegion(nPort.qdPort.port, clipRegion);
505             if (forUpdate) {
506                 // AppKit may have tried to help us by doing a BeginUpdate.
507                 // But the invalid region at that level didn't include AppKit's notion of what was not valid.
508                 // We reset the port's visible region to counteract what BeginUpdate did.
509                 SetPortVisibleRegion(nPort.qdPort.port, clipRegion);
510                 InvalWindowRgn(windowRef, clipRegion);
511             }
512             
513             qdPortState->forUpdate = forUpdate;
514             break;
515         }
516 #endif /* NP_NO_QUICKDRAW */
518         case NPDrawingModelCoreGraphics: {            
519             ASSERT([NSView focusView] == self);
521             CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
523             PortState_CG *cgPortState = (PortState_CG *)malloc(sizeof(PortState_CG));
524             portState = (PortState)cgPortState;
525             cgPortState->context = context;
526             
527             // Update the plugin's window/context
528 #ifdef NP_NO_CARBON
529             nPort.cgPort.window = (NPNSWindow *)[self currentWindow];
530 #else
531             nPort.cgPort.window = _eventHandler->platformWindow([self currentWindow]);
532 #endif /* NP_NO_CARBON */
533             nPort.cgPort.context = context;
534             window.window = &nPort.cgPort;
536             // Save current graphics context's state; will be restored by -restorePortState:
537             CGContextSaveGState(context);
539             // Clip to the dirty region if drawing to a window. When drawing to another bitmap context, do not clip.
540             if ([NSGraphicsContext currentContext] == [[self currentWindow] graphicsContext]) {
541                 // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
542                 // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
543                 // knows about the true set of dirty rects.
544                 NSView *opaqueAncestor = [self opaqueAncestor];
545                 const NSRect *dirtyRects;
546                 NSInteger count;
547                 [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&count];
548                 Vector<CGRect, 16> convertedDirtyRects;
549                 convertedDirtyRects.resize(count);
550                 for (int i = 0; i < count; ++i)
551                     reinterpret_cast<NSRect&>(convertedDirtyRects[i]) = [self convertRect:dirtyRects[i] fromView:opaqueAncestor];
552                 CGContextClipToRects(context, convertedDirtyRects.data(), count);
553             }
555             break;
556         }
557         
558         default:
559             ASSERT_NOT_REACHED();
560             portState = NULL;
561             break;
562     }
563     
564     return portState;
567 - (PortState)saveAndSetNewPortState
569     return [self saveAndSetNewPortStateForUpdate:NO];
572 - (void)restorePortState:(PortState)portState
574     if (drawingModel == NPDrawingModelCoreAnimation)
575         return;
577     ASSERT([self currentWindow]);
578     ASSERT(portState);
579     
580     switch (drawingModel) {
581 #ifndef NP_NO_QUICKDRAW
582         case NPDrawingModelQuickDraw: {
583             PortState_QD *qdPortState = (PortState_QD *)portState;
584             WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
585             CGrafPtr port = GetWindowPort(windowRef);
587             SetPort(port);
589             if (qdPortState->forUpdate)
590                 ValidWindowRgn(windowRef, qdPortState->clipRegion);
592             SetOrigin(qdPortState->oldOrigin.h, qdPortState->oldOrigin.v);
594             SetPortClipRegion(port, qdPortState->oldClipRegion);
595             if (qdPortState->forUpdate)
596                 SetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
598             DisposeRgn(qdPortState->oldClipRegion);
599             DisposeRgn(qdPortState->oldVisibleRegion);
600             DisposeRgn(qdPortState->clipRegion);
602             SetGWorld(qdPortState->oldPort, qdPortState->oldDevice);
603             break;
604         }
605 #endif /* NP_NO_QUICKDRAW */
606         
607         case NPDrawingModelCoreGraphics:
608             ASSERT([NSView focusView] == self);
609             ASSERT(((PortState_CG *)portState)->context == nPort.cgPort.context);
610             CGContextRestoreGState(nPort.cgPort.context);
611             break;
613         default:
614             ASSERT_NOT_REACHED();
615             break;
616     }
619 - (BOOL)sendEvent:(void*)event isDrawRect:(BOOL)eventIsDrawRect
621     if (![self window])
622         return NO;
623     ASSERT(event);
624        
625     if (!isStarted)
626         return NO;
628     ASSERT(NPP_HandleEvent);
629     
630     // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
631     // We probably don't want more general reentrancy protection; we are really
632     // protecting only against this one case, which actually comes up when
633     // you first install the SVG viewer plug-in.
634     if (inSetWindow)
635         return NO;
637     Frame* frame = core([self webFrame]);
638     if (!frame)
639         return NO;
640     Page* page = frame->page();
641     if (!page)
642         return NO;
644     bool wasDeferring = page->defersLoading();
645     if (!wasDeferring)
646         page->setDefersLoading(true);
648     // Can only send drawRect (updateEvt) to CoreGraphics plugins when actually drawing
649     ASSERT((drawingModel != NPDrawingModelCoreGraphics) || !eventIsDrawRect || [NSView focusView] == self);
650     
651     PortState portState = NULL;
652     
653     if (isDrawingModelQuickDraw(drawingModel) || (drawingModel != NPDrawingModelCoreAnimation && eventIsDrawRect)) {
654         // In CoreGraphics mode, the port state only needs to be saved/set when redrawing the plug-in view.
655         // The plug-in is not allowed to draw at any other time.
656         portState = [self saveAndSetNewPortStateForUpdate:eventIsDrawRect];
657         // We may have changed the window, so inform the plug-in.
658         [self setWindowIfNecessary];
659     }
660     
661 #if !defined(NDEBUG) && !defined(NP_NO_QUICKDRAW)
662     // Draw green to help debug.
663     // If we see any green we know something's wrong.
664     // Note that PaintRect() only works for QuickDraw plugins; otherwise the current QD port is undefined.
665     if (isDrawingModelQuickDraw(drawingModel) && eventIsDrawRect) {
666         ForeColor(greenColor);
667         const ::Rect bigRect = { -10000, -10000, 10000, 10000 };
668         PaintRect(&bigRect);
669         ForeColor(blackColor);
670     }
671 #endif
672     
673     // Temporarily retain self in case the plug-in view is released while sending an event. 
674     [[self retain] autorelease];
675     
676     BOOL acceptedEvent;
677     [self willCallPlugInFunction];
678     {
679         JSC::JSLock::DropAllLocks dropAllLocks(false);
680         acceptedEvent = NPP_HandleEvent(plugin, event);
681     }
682     [self didCallPlugInFunction];
683         
684     if (portState) {
685         if ([self currentWindow])
686             [self restorePortState:portState];
687         free(portState);
688     }
690     if (!wasDeferring)
691         page->setDefersLoading(false);
692             
693     return acceptedEvent;
696 - (void)sendActivateEvent:(BOOL)activate
698     if (!isStarted)
699         return;
701     _eventHandler->windowFocusChanged(activate);
704 - (void)sendDrawRectEvent:(NSRect)rect
706     ASSERT(_eventHandler);
707     
708     _eventHandler->drawRect(rect);
711 - (void)stopTimers
713     if (_eventHandler)
714         _eventHandler->stopTimers();
715     
716     shouldFireTimers = NO;
717     
718     if (!timers)
719         return;
721     HashMap<uint32, PluginTimer*>::const_iterator end = timers->end();
722     for (HashMap<uint32, PluginTimer*>::const_iterator it = timers->begin(); it != end; ++it) {
723         PluginTimer* timer = it->second;
724         timer->stop();
725     }    
728 - (void)restartTimers
730     ASSERT([self window]);
731     
732     if (shouldFireTimers)
733         [self stopTimers];
734     
735     if (!isStarted || [[self window] isMiniaturized])
736         return;
738     shouldFireTimers = YES;
739     
740     // If the plugin is completely obscured (scrolled out of view, for example), then we will
741     // send null events at a reduced rate.
742     _eventHandler->startTimers(isCompletelyObscured);
743     
744     if (!timers)
745         return;
746     
747     HashMap<uint32, PluginTimer*>::const_iterator end = timers->end();
748     for (HashMap<uint32, PluginTimer*>::const_iterator it = timers->begin(); it != end; ++it) {
749         PluginTimer* timer = it->second;
750         ASSERT(!timer->isActive());
751         timer->start(isCompletelyObscured);
752     }    
755 - (BOOL)acceptsFirstResponder
757     return YES;
760 - (void)setHasFocus:(BOOL)flag
762     if (!isStarted)
763         return;
765     if (hasFocus == flag)
766         return;
767     
768     hasFocus = flag;
769     
770     // We need to null check the event handler here because
771     // the plug-in view can resign focus after it's been stopped
772     // and the event handler has been deleted.
773     if (_eventHandler)
774         _eventHandler->focusChanged(hasFocus);    
777 - (BOOL)becomeFirstResponder
779     [self setHasFocus:YES];
780     return YES;
783 - (BOOL)resignFirstResponder
785     [self setHasFocus:NO];    
786     return YES;
789 // AppKit doesn't call mouseDown or mouseUp on right-click. Simulate control-click
790 // mouseDown and mouseUp so plug-ins get the right-click event as they do in Carbon (3125743).
791 - (void)rightMouseDown:(NSEvent *)theEvent
793     [self mouseDown:theEvent];
796 - (void)rightMouseUp:(NSEvent *)theEvent
798     [self mouseUp:theEvent];
801 - (void)mouseDown:(NSEvent *)theEvent
803     if (!isStarted)
804         return;
806     _eventHandler->mouseDown(theEvent);
809 - (void)mouseUp:(NSEvent *)theEvent
811     if (!isStarted)
812         return;
814     _eventHandler->mouseUp(theEvent);
817 - (void)mouseEntered:(NSEvent *)theEvent
819     if (!isStarted)
820         return;
822     _eventHandler->mouseEntered(theEvent);
825 - (void)mouseExited:(NSEvent *)theEvent
827     if (!isStarted)
828         return;
830     _eventHandler->mouseExited(theEvent);
831     
832     // 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
833     // current cursor is otherwise.  Therefore we have no choice but to unconditionally reset the cursor when the mouse exits the plugin.
834     [[NSCursor arrowCursor] set];
837 // We can't name this method mouseMoved because we don't want to override 
838 // the NSView mouseMoved implementation.
839 - (void)handleMouseMoved:(NSEvent *)theEvent
841     if (!isStarted)
842         return;
844     _eventHandler->mouseMoved(theEvent);
846     
847 - (void)mouseDragged:(NSEvent *)theEvent
849     if (!isStarted)
850         return;
852     _eventHandler->mouseDragged(theEvent);
855 - (void)scrollWheel:(NSEvent *)theEvent
857     if (!isStarted) {
858         [super scrollWheel:theEvent];
859         return;
860     }
862     if (!_eventHandler->scrollWheel(theEvent))
863         [super scrollWheel:theEvent];
866 - (void)keyUp:(NSEvent *)theEvent
868     if (!isStarted)
869         return;
871     _eventHandler->keyUp(theEvent);
874 - (void)keyDown:(NSEvent *)theEvent
876     if (!isStarted)
877         return;
879     _eventHandler->keyDown(theEvent);
882 - (void)flagsChanged:(NSEvent *)theEvent
884     if (!isStarted)
885         return;
887     _eventHandler->flagsChanged(theEvent);
890 - (void)cut:(id)sender
892     if (!isStarted)
893         return;
895     _eventHandler->keyDown([NSApp currentEvent]);
898 - (void)copy:(id)sender
900     if (!isStarted)
901         return;
903     _eventHandler->keyDown([NSApp currentEvent]);
906 - (void)paste:(id)sender
908     if (!isStarted)
909         return;
911     _eventHandler->keyDown([NSApp currentEvent]);
914 - (void)selectAll:(id)sender
916     if (!isStarted)
917         return;
919     _eventHandler->keyDown([NSApp currentEvent]);
922 #pragma mark WEB_NETSCAPE_PLUGIN
924 - (BOOL)isNewWindowEqualToOldWindow
926     ASSERT(drawingModel != NPDrawingModelCoreAnimation);
927         
928     if (window.x != lastSetWindow.x)
929         return NO;
930     if (window.y != lastSetWindow.y)
931         return NO;
932     if (window.width != lastSetWindow.width)
933         return NO;
934     if (window.height != lastSetWindow.height)
935         return NO;
936     if (window.clipRect.top != lastSetWindow.clipRect.top)
937         return NO;
938     if (window.clipRect.left != lastSetWindow.clipRect.left)
939         return NO;
940     if (window.clipRect.bottom  != lastSetWindow.clipRect.bottom)
941         return NO;
942     if (window.clipRect.right != lastSetWindow.clipRect.right)
943         return NO;
944     if (window.type != lastSetWindow.type)
945         return NO;
946     
947     switch (drawingModel) {
948 #ifndef NP_NO_QUICKDRAW
949         case NPDrawingModelQuickDraw:
950             if (nPort.qdPort.portx != lastSetPort.qdPort.portx)
951                 return NO;
952             if (nPort.qdPort.porty != lastSetPort.qdPort.porty)
953                 return NO;
954             if (nPort.qdPort.port != lastSetPort.qdPort.port)
955                 return NO;
956         break;
957 #endif /* NP_NO_QUICKDRAW */
958             
959         case NPDrawingModelCoreGraphics:
960             if (nPort.cgPort.window != lastSetPort.cgPort.window)
961                 return NO;
962             if (nPort.cgPort.context != lastSetPort.cgPort.context)
963                 return NO;
964         break;
965                     
966         default:
967             ASSERT_NOT_REACHED();
968         break;
969     }
970     
971     return YES;
974 - (void)updateAndSetWindow
976     ASSERT(drawingModel != NPDrawingModelCoreAnimation);
978     // A plug-in can only update if it's (1) already been started (2) isn't stopped
979     // and (3) is able to draw on-screen. To meet condition (3) the plug-in must not
980     // be hidden and be attached to a window. QuickDraw plug-ins are an important
981     // excpetion to rule (3) because they manually must be told when to stop writing
982     // bits to the window backing store, thus to do so requires a new call to
983     // NPP_SetWindow() with an empty NPWindow struct.
984     if (!isStarted)
985         return;
986 #ifdef NP_NO_QUICKDRAW
987     if (![self canDraw])
988         return;
989 #else
990     if (drawingModel != NPDrawingModelQuickDraw && ![self canDraw])
991         return;
992 #endif // NP_NO_QUICKDRAW
993     
994     BOOL didLockFocus = [NSView focusView] != self && [self lockFocusIfCanDraw];
995     
996     if (drawingModel == NPDrawingModelCoreGraphics || isDrawingModelQuickDraw(drawingModel)) {
997         [self setWindowIfNecessary];
998         if (didLockFocus)
999             [self unlockFocus];
1001         return;
1002     }
1003     
1004     PortState portState = [self saveAndSetNewPortState];
1005     if (portState) {
1006         [self setWindowIfNecessary];
1007         [self restorePortState:portState];
1008         free(portState);
1009     }   
1010     if (didLockFocus)
1011         [self unlockFocus];
1014 - (void)setWindowIfNecessary
1016     ASSERT(drawingModel != NPDrawingModelCoreAnimation);
1017            
1018     if (!isStarted) {
1019         return;
1020     }
1021     
1022     if (![self isNewWindowEqualToOldWindow]) {        
1023         // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
1024         // We probably don't want more general reentrancy protection; we are really
1025         // protecting only against this one case, which actually comes up when
1026         // you first install the SVG viewer plug-in.
1027         NPError npErr;
1028         ASSERT(!inSetWindow);
1029         
1030         inSetWindow = YES;
1031         
1032         // A CoreGraphics plugin's window may only be set while the plugin is being updated
1033         ASSERT((drawingModel != NPDrawingModelCoreGraphics) || [NSView focusView] == self);
1034         
1035         [self willCallPlugInFunction];
1036         {
1037             JSC::JSLock::DropAllLocks dropAllLocks(false);
1038             npErr = NPP_SetWindow(plugin, &window);
1039         }
1040         [self didCallPlugInFunction];
1041         inSetWindow = NO;
1043 #ifndef NDEBUG
1044         switch (drawingModel) {
1045 #ifndef NP_NO_QUICKDRAW
1046             case NPDrawingModelQuickDraw:
1047                 LOG(Plugins, "NPP_SetWindow (QuickDraw): %d, port=0x%08x, window.x:%d window.y:%d window.width:%d window.height:%d",
1048                 npErr, (int)nPort.qdPort.port, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1049             break;
1050 #endif /* NP_NO_QUICKDRAW */
1051             
1052             case NPDrawingModelCoreGraphics:
1053                 LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d",
1054                 npErr, nPort.cgPort.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1055             break;
1056                         
1057             default:
1058                 ASSERT_NOT_REACHED();
1059             break;
1060         }
1061 #endif /* !defined(NDEBUG) */
1062         
1063         lastSetWindow = window;
1064         lastSetPort = nPort;
1065     }
1068 - (void)removeTrackingRect
1070     if (trackingTag) {
1071         [self removeTrackingRect:trackingTag];
1072         trackingTag = 0;
1074         // Do the following after setting trackingTag to 0 so we don't re-enter.
1076         // Balance the retain in resetTrackingRect. Use autorelease in case we hold 
1077         // the last reference to the window during tear-down, to avoid crashing AppKit. 
1078         [[self window] autorelease];
1079     }
1082 - (void)resetTrackingRect
1084     [self removeTrackingRect];
1085     if (isStarted) {
1086         // Retain the window so that removeTrackingRect can work after the window is closed.
1087         [[self window] retain];
1088         trackingTag = [self addTrackingRect:[self bounds] owner:self userData:nil assumeInside:NO];
1089     }
1092 + (void)setCurrentPluginView:(WebBaseNetscapePluginView *)view
1094     currentPluginView = view;
1097 + (WebBaseNetscapePluginView *)currentPluginView
1099     return currentPluginView;
1102 - (BOOL)canStart
1104     return YES;
1107 - (void)didStart
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     } 
1123 - (void)addWindowObservers
1125     ASSERT([self window]);
1127     NSWindow *theWindow = [self window];
1128     
1129     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
1130     [notificationCenter addObserver:self selector:@selector(windowWillClose:) 
1131                                name:NSWindowWillCloseNotification object:theWindow]; 
1132     [notificationCenter addObserver:self selector:@selector(windowBecameKey:)
1133                                name:NSWindowDidBecomeKeyNotification object:theWindow];
1134     [notificationCenter addObserver:self selector:@selector(windowResignedKey:)
1135                                name:NSWindowDidResignKeyNotification object:theWindow];
1136     [notificationCenter addObserver:self selector:@selector(windowDidMiniaturize:)
1137                                name:NSWindowDidMiniaturizeNotification object:theWindow];
1138     [notificationCenter addObserver:self selector:@selector(windowDidDeminiaturize:)
1139                                name:NSWindowDidDeminiaturizeNotification object:theWindow];
1140     
1141     [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchFromUser:)
1142                                name:LoginWindowDidSwitchFromUserNotification object:nil];
1143     [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchToUser:)
1144                                name:LoginWindowDidSwitchToUserNotification object:nil];
1147 - (void)removeWindowObservers
1149     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
1150     [notificationCenter removeObserver:self name:NSWindowWillCloseNotification        object:nil]; 
1151     [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification     object:nil];
1152     [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification     object:nil];
1153     [notificationCenter removeObserver:self name:NSWindowDidMiniaturizeNotification   object:nil];
1154     [notificationCenter removeObserver:self name:NSWindowDidDeminiaturizeNotification object:nil];
1155     [notificationCenter removeObserver:self name:LoginWindowDidSwitchFromUserNotification   object:nil];
1156     [notificationCenter removeObserver:self name:LoginWindowDidSwitchToUserNotification     object:nil];
1159 - (BOOL)start
1161     ASSERT([self currentWindow]);
1162     
1163     if (isStarted)
1164         return YES;
1166     if (![self canStart])
1167         return NO;
1168     
1169     ASSERT([self webView]);
1170     
1171     if (![[[self webView] preferences] arePlugInsEnabled])
1172         return NO;
1174     // Open the plug-in package so it remains loaded while our plugin uses it
1175     [_pluginPackage.get() open];
1176     
1177     // Initialize drawingModel to an invalid value so that we can detect when the plugin does not specify a drawingModel
1178     drawingModel = (NPDrawingModel)-1;
1179     
1180     // Initialize eventModel to an invalid value so that we can detect when the plugin does not specify an event model.
1181     eventModel = (NPEventModel)-1;
1182     
1183     NPError npErr = [self _createPlugin];
1184     if (npErr != NPERR_NO_ERROR) {
1185         LOG_ERROR("NPP_New failed with error: %d", npErr);
1186         [self _destroyPlugin];
1187         [_pluginPackage.get() close];
1188         return NO;
1189     }
1190     
1191     if (drawingModel == (NPDrawingModel)-1) {
1192 #ifndef NP_NO_QUICKDRAW
1193         // Default to QuickDraw if the plugin did not specify a drawing model.
1194         drawingModel = NPDrawingModelQuickDraw;
1195 #else
1196         // QuickDraw is not available, so we can't default to it. Instead, default to CoreGraphics.
1197         drawingModel = NPDrawingModelCoreGraphics;
1198 #endif
1199     }
1201     if (eventModel == (NPEventModel)-1) {
1202         // If the plug-in did not specify a drawing model we default to Carbon when it is available.
1203 #ifndef NP_NO_CARBON
1204         eventModel = NPEventModelCarbon;
1205 #else
1206         eventModel = NPEventModelCocoa;
1207 #endif // NP_NO_CARBON
1208     }
1210 #ifndef NP_NO_CARBON
1211     if (eventModel == NPEventModelCocoa && isDrawingModelQuickDraw(drawingModel)) {
1212         LOG(Plugins, "Plugin can't use use Cocoa event model with QuickDraw drawing model: %@", _pluginPackage.get());
1213         [self _destroyPlugin];
1214         [_pluginPackage.get() close];
1215         
1216         return NO;
1217     }        
1218 #endif // NP_NO_CARBON
1219     
1220 #ifndef BUILDING_ON_TIGER
1221     if (drawingModel == NPDrawingModelCoreAnimation) {
1222         void *value = 0;
1223         if (NPP_GetValue(plugin, NPPVpluginCoreAnimationLayer, &value) == NPERR_NO_ERROR && value) {
1224             
1225             // The plug-in gives us a retained layer.
1226             _layer.adoptNS((CALayer *)value);
1227             [self setWantsLayer:YES];
1228             [self setLayer:_layer.get()];
1229             LOG(Plugins, "%@ is using Core Animation drawing model with layer %@", _pluginPackage.get(), _layer.get());
1230         }
1232         ASSERT(_layer);
1233     }
1234 #endif
1235     
1236     // Create the event handler
1237     _eventHandler.set(WebNetscapePluginEventHandler::create(self));
1238     
1239     // Get the text input vtable
1240     if (eventModel == NPEventModelCocoa) {
1241         [self willCallPlugInFunction];
1242         {
1243             JSC::JSLock::DropAllLocks dropAllLocks(false);
1244             NPPluginTextInputFuncs *value;
1245             if (NPP_GetValue(plugin, NPPVpluginTextInputFuncs, &value) == NPERR_NO_ERROR && value)
1246                 textInputFuncs = value;
1247         }
1248         [self didCallPlugInFunction];
1249     }
1250     
1251     isStarted = YES;
1252     [[self webView] addPluginInstanceView:self];
1254     if (drawingModel == NPDrawingModelCoreGraphics || isDrawingModelQuickDraw(drawingModel))
1255         [self updateAndSetWindow];
1257     if ([self window]) {
1258         [self addWindowObservers];
1259         if ([[self window] isKeyWindow]) {
1260             [self sendActivateEvent:YES];
1261         }
1262         [self restartTimers];
1263     }
1265     [self resetTrackingRect];
1266     
1267     [self didStart];
1268     
1269     return YES;
1272 - (void)stop
1274     // If we're already calling a plug-in function, do not call NPP_Destroy().  The plug-in function we are calling
1275     // may assume that its instance->pdata, or other memory freed by NPP_Destroy(), is valid and unchanged until said
1276     // plugin-function returns.
1277     // See <rdar://problem/4480737>.
1278     if (pluginFunctionCallDepth > 0) {
1279         shouldStopSoon = YES;
1280         return;
1281     }
1282     
1283     [self removeTrackingRect];
1285     if (!isStarted)
1286         return;
1287     
1288     isStarted = NO;
1289     
1290     [[self webView] removePluginInstanceView:self];
1292     // To stop active streams it's necessary to invoke stop() on a copy 
1293     // of streams. This is because calling WebNetscapePluginStream::stop() also has the side effect
1294     // of removing a stream from this hash set.
1295     Vector<RefPtr<WebNetscapePluginStream> > streamsCopy;
1296     copyToVector(streams, streamsCopy);
1297     for (size_t i = 0; i < streamsCopy.size(); i++)
1298         streamsCopy[i]->stop();
1299     
1300     // Stop the timers
1301     [self stopTimers];
1302     
1303     // Stop notifications and callbacks.
1304     [self removeWindowObservers];
1305     [[_pendingFrameLoads.get() allKeys] makeObjectsPerformSelector:@selector(_setInternalLoadDelegate:) withObject:nil];
1306     [NSObject cancelPreviousPerformRequestsWithTarget:self];
1308     // Setting the window type to 0 ensures that NPP_SetWindow will be called if the plug-in is restarted.
1309     lastSetWindow.type = (NPWindowType)0;
1310     
1311     _layer = 0;
1312     
1313     [self _destroyPlugin];
1314     [_pluginPackage.get() close];
1315     
1316     _eventHandler.clear();
1317     
1318     textInputFuncs = 0;
1321 - (BOOL)isStarted
1323     return isStarted;
1326 - (NPEventModel)eventModel
1328     return eventModel;
1331 - (WebDataSource *)dataSource
1333     WebFrame *webFrame = kit(core(_element.get())->document()->frame());
1334     return [webFrame _dataSource];
1337 - (WebFrame *)webFrame
1339     return [[self dataSource] webFrame];
1342 - (WebView *)webView
1344     return [[self webFrame] webView];
1347 - (NSWindow *)currentWindow
1349     return [self window] ? [self window] : [[self webView] hostWindow];
1352 - (NPP)plugin
1354     return plugin;
1357 - (WebNetscapePluginPackage *)pluginPackage
1359     return _pluginPackage.get();
1362 - (void)setPluginPackage:(WebNetscapePluginPackage *)thePluginPackage;
1364     _pluginPackage = thePluginPackage;
1366     NPP_New =           [_pluginPackage.get() NPP_New];
1367     NPP_Destroy =       [_pluginPackage.get() NPP_Destroy];
1368     NPP_SetWindow =     [_pluginPackage.get() NPP_SetWindow];
1369     NPP_NewStream =     [_pluginPackage.get() NPP_NewStream];
1370     NPP_WriteReady =    [_pluginPackage.get() NPP_WriteReady];
1371     NPP_Write =         [_pluginPackage.get() NPP_Write];
1372     NPP_StreamAsFile =  [_pluginPackage.get() NPP_StreamAsFile];
1373     NPP_DestroyStream = [_pluginPackage.get() NPP_DestroyStream];
1374     NPP_HandleEvent =   [_pluginPackage.get() NPP_HandleEvent];
1375     NPP_URLNotify =     [_pluginPackage.get() NPP_URLNotify];
1376     NPP_GetValue =      [_pluginPackage.get() NPP_GetValue];
1377     NPP_SetValue =      [_pluginPackage.get() NPP_SetValue];
1378     NPP_Print =         [_pluginPackage.get() NPP_Print];
1381 - (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values;
1383     ASSERT([keys count] == [values count]);
1384     
1385     // Convert the attributes to 2 C string arrays.
1386     // These arrays are passed to NPP_New, but the strings need to be
1387     // modifiable and live the entire life of the plugin.
1389     // The Java plug-in requires the first argument to be the base URL
1390     if ([_MIMEType.get() isEqualToString:@"application/x-java-applet"]) {
1391         cAttributes = (char **)malloc(([keys count] + 1) * sizeof(char *));
1392         cValues = (char **)malloc(([values count] + 1) * sizeof(char *));
1393         cAttributes[0] = strdup("DOCBASE");
1394         cValues[0] = strdup([_baseURL.get() _web_URLCString]);
1395         argsCount++;
1396     } else {
1397         cAttributes = (char **)malloc([keys count] * sizeof(char *));
1398         cValues = (char **)malloc([values count] * sizeof(char *));
1399     }
1401     BOOL isWMP = [[[_pluginPackage.get() bundle] bundleIdentifier] isEqualToString:@"com.microsoft.WMP.defaultplugin"];
1402     
1403     unsigned i;
1404     unsigned count = [keys count];
1405     for (i = 0; i < count; i++) {
1406         NSString *key = [keys objectAtIndex:i];
1407         NSString *value = [values objectAtIndex:i];
1408         if ([key _webkit_isCaseInsensitiveEqualToString:@"height"]) {
1409             specifiedHeight = [value intValue];
1410         } else if ([key _webkit_isCaseInsensitiveEqualToString:@"width"]) {
1411             specifiedWidth = [value intValue];
1412         }
1413         // Avoid Window Media Player crash when these attributes are present.
1414         if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) {
1415             continue;
1416         }
1417         cAttributes[argsCount] = strdup([key UTF8String]);
1418         cValues[argsCount] = strdup([value UTF8String]);
1419         LOG(Plugins, "%@ = %@", key, value);
1420         argsCount++;
1421     }
1424 #pragma mark NSVIEW
1426 - (id)initWithFrame:(NSRect)frame
1427       pluginPackage:(WebNetscapePluginPackage *)thePluginPackage
1428                 URL:(NSURL *)theURL
1429             baseURL:(NSURL *)theBaseURL
1430            MIMEType:(NSString *)MIME
1431       attributeKeys:(NSArray *)keys
1432     attributeValues:(NSArray *)values
1433        loadManually:(BOOL)loadManually
1434          DOMElement:(DOMElement *)anElement
1436     [super initWithFrame:frame];
1438     _pendingFrameLoads.adoptNS([[NSMutableDictionary alloc] init]);
1439     
1440     // load the plug-in if it is not already loaded
1441     if (![thePluginPackage load]) {
1442         [self release];
1443         return nil;
1444     }
1445     [self setPluginPackage:thePluginPackage];
1446     
1447     _element = anElement;
1448     _sourceURL.adoptNS([theURL copy]);
1449     _baseURL.adoptNS([theBaseURL copy]);
1450     
1451     [self setAttributeKeys:keys andValues:values];
1452     if (loadManually)
1453         _mode = NP_FULL;
1454     else
1455         _mode = NP_EMBED;
1456     
1457     _loadManually = loadManually;
1458     return self;
1461 - (id)initWithFrame:(NSRect)frame
1463     ASSERT_NOT_REACHED();
1464     return nil;
1467 - (void)fini
1469 #ifndef NP_NO_QUICKDRAW
1470     if (offscreenGWorld)
1471         DisposeGWorld(offscreenGWorld);
1472 #endif
1474     for (unsigned i = 0; i < argsCount; i++) {
1475         free(cAttributes[i]);
1476         free(cValues[i]);
1477     }
1478     free(cAttributes);
1479     free(cValues);
1480     
1481     ASSERT(!_eventHandler);
1482     
1483     if (timers) {
1484         deleteAllValues(*timers);
1485         delete timers;
1486     }    
1489 - (void)disconnectStream:(WebNetscapePluginStream*)stream
1491     streams.remove(stream);
1494 - (void)dealloc
1496     ASSERT(!isStarted);
1497     ASSERT(!plugin);
1499     [self fini];
1501     [super dealloc];
1504 - (void)finalize
1506     ASSERT_MAIN_THREAD();
1507     ASSERT(!isStarted);
1509     [self fini];
1511     [super finalize];
1514 - (void)drawRect:(NSRect)rect
1516     if (drawingModel == NPDrawingModelCoreAnimation)
1517         return;
1519     if (!isStarted)
1520         return;
1521     
1522     if ([NSGraphicsContext currentContextDrawingToScreen])
1523         [self sendDrawRectEvent:rect];
1524     else {
1525         NSBitmapImageRep *printedPluginBitmap = [self _printedPluginBitmap];
1526         if (printedPluginBitmap) {
1527             // Flip the bitmap before drawing because the QuickDraw port is flipped relative
1528             // to this view.
1529             CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
1530             CGContextSaveGState(cgContext);
1531             NSRect bounds = [self bounds];
1532             CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
1533             CGContextScaleCTM(cgContext, 1.0f, -1.0f);
1534             [printedPluginBitmap drawInRect:bounds];
1535             CGContextRestoreGState(cgContext);
1536         }
1537     }
1540 - (BOOL)isFlipped
1542     return YES;
1545 - (void)renewGState
1547     [super renewGState];
1548     
1549     // -renewGState is called whenever the view's geometry changes.  It's a little hacky to override this method, but
1550     // much safer than walking up the view hierarchy and observing frame/bounds changed notifications, since you don't
1551     // have to track subsequent changes to the view hierarchy and add/remove notification observers.
1552     // NSOpenGLView uses the exact same technique to reshape its OpenGL surface.
1553     [self _viewHasMoved];
1556 -(void)tellQuickTimeToChill
1558 #ifndef NP_NO_QUICKDRAW
1559     ASSERT(isDrawingModelQuickDraw(drawingModel));
1560     
1561     // Make a call to the secret QuickDraw API that makes QuickTime calm down.
1562     WindowRef windowRef = (WindowRef)[[self window] windowRef];
1563     if (!windowRef) {
1564         return;
1565     }
1566     CGrafPtr port = GetWindowPort(windowRef);
1567     ::Rect bounds;
1568     GetPortBounds(port, &bounds);
1569     WKCallDrawingNotification(port, &bounds);
1570 #endif /* NP_NO_QUICKDRAW */
1573 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
1575     if (isDrawingModelQuickDraw(drawingModel))
1576         [self tellQuickTimeToChill];
1578     // We must remove the tracking rect before we move to the new window.
1579     // Once we move to the new window, it will be too late.
1580     [self removeTrackingRect];
1581     [self removeWindowObservers];
1582     
1583     // Workaround for: <rdar://problem/3822871> resignFirstResponder is not sent to first responder view when it is removed from the window
1584     [self setHasFocus:NO];
1586     if (!newWindow) {
1587         if ([[self webView] hostWindow]) {
1588             // View will be moved out of the actual window but it still has a host window.
1589             [self stopTimers];
1590         } else {
1591             // View will have no associated windows.
1592             [self stop];
1594             // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
1595             // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
1596             [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1597         }
1598     }
1601 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
1603     if (!newSuperview) {
1604         // Stop the plug-in when it is removed from its superview.  It is not sufficient to do this in -viewWillMoveToWindow:nil, because
1605         // the WebView might still has a hostWindow at that point, which prevents the plug-in from being destroyed.
1606         // There is no need to start the plug-in when moving into a superview.  -viewDidMoveToWindow takes care of that.
1607         [self stop];
1608         
1609         // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
1610         // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
1611         [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1612     }
1615 - (void)viewDidMoveToWindow
1617     [self resetTrackingRect];
1618     
1619     if ([self window]) {
1620         // While in the view hierarchy, observe WebPreferencesChangedNotification so that we can start/stop depending
1621         // on whether plugins are enabled.
1622         [[NSNotificationCenter defaultCenter] addObserver:self
1623                                               selector:@selector(preferencesHaveChanged:)
1624                                               name:WebPreferencesChangedNotification
1625                                               object:nil];
1627         // View moved to an actual window. Start it if not already started.
1628         [self start];
1629         [self restartTimers];
1630         [self addWindowObservers];
1631     } else if ([[self webView] hostWindow]) {
1632         // View moved out of an actual window, but still has a host window.
1633         // Call setWindow to explicitly "clip out" the plug-in from sight.
1634         // FIXME: It would be nice to do this where we call stopNullEvents in viewWillMoveToWindow.
1635         [self updateAndSetWindow];
1636     }
1639 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
1641     if (!hostWindow && ![self window]) {
1642         // View will have no associated windows.
1643         [self stop];
1645         // Remove WebPreferencesChangedNotification observer -- we will observe once again when we move back into the window
1646         [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1647     }
1650 - (void)viewDidMoveToHostWindow
1652     if ([[self webView] hostWindow]) {
1653         // View now has an associated window. Start it if not already started.
1654         [self start];
1655     }
1658 #pragma mark NOTIFICATIONS
1660 - (void)windowWillClose:(NSNotification *)notification 
1662     [self stop]; 
1665 - (void)windowBecameKey:(NSNotification *)notification
1667     [self sendActivateEvent:YES];
1668     [self setNeedsDisplay:YES];
1669     [self restartTimers];
1670 #ifndef NP_NO_CARBON
1671     SetUserFocusWindow((WindowRef)[[self window] windowRef]);
1672 #endif // NP_NO_CARBON
1675 - (void)windowResignedKey:(NSNotification *)notification
1677     [self sendActivateEvent:NO];
1678     [self setNeedsDisplay:YES];
1679     [self restartTimers];
1682 - (void)windowDidMiniaturize:(NSNotification *)notification
1684     [self stopTimers];
1687 - (void)windowDidDeminiaturize:(NSNotification *)notification
1689     [self stopTimers];
1692 - (void)loginWindowDidSwitchFromUser:(NSNotification *)notification
1694     [self stopTimers];
1697 -(void)loginWindowDidSwitchToUser:(NSNotification *)notification
1699     [self restartTimers];
1702 - (void)preferencesHaveChanged:(NSNotification *)notification
1704     WebPreferences *preferences = [[self webView] preferences];
1705     BOOL arePlugInsEnabled = [preferences arePlugInsEnabled];
1706     
1707     if ([notification object] == preferences && isStarted != arePlugInsEnabled) {
1708         if (arePlugInsEnabled) {
1709             if ([self currentWindow]) {
1710                 [self start];
1711             }
1712         } else {
1713             [self stop];
1714             [self setNeedsDisplay:YES];
1715         }
1716     }
1719 - (NPObject *)createPluginScriptableObject
1721     if (!NPP_GetValue || ![self isStarted])
1722         return NULL;
1723         
1724     NPObject *value = NULL;
1725     NPError error;
1726     [self willCallPlugInFunction];
1727     {
1728         JSC::JSLock::DropAllLocks dropAllLocks(false);
1729         error = NPP_GetValue(plugin, NPPVpluginScriptableNPObject, &value);
1730     }
1731     [self didCallPlugInFunction];
1732     if (error != NPERR_NO_ERROR)
1733         return NULL;
1734     
1735     return value;
1738 - (void)willCallPlugInFunction
1740     ASSERT(plugin);
1742     // Could try to prevent infinite recursion here, but it's probably not worth the effort.
1743     pluginFunctionCallDepth++;
1746 - (void)didCallPlugInFunction
1748     ASSERT(pluginFunctionCallDepth > 0);
1749     pluginFunctionCallDepth--;
1750     
1751     // If -stop was called while we were calling into a plug-in function, and we're no longer
1752     // inside a plug-in function, stop now.
1753     if (pluginFunctionCallDepth == 0 && shouldStopSoon) {
1754         shouldStopSoon = NO;
1755         [self stop];
1756     }
1759 -(void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
1761     ASSERT(_loadManually);
1762     ASSERT(!_manualStream);
1764     _manualStream = WebNetscapePluginStream::create(core([self webFrame])->loader());
1767 - (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
1769     ASSERT(_loadManually);
1770     ASSERT(_manualStream);
1771     
1772     _dataLengthReceived += [data length];
1773     
1774     if (![self isStarted])
1775         return;
1777     if (!_manualStream->plugin()) {
1779         _manualStream->setRequestURL([[[self dataSource] request] URL]);
1780         _manualStream->setPlugin([self plugin]);
1781         ASSERT(_manualStream->plugin());
1782         
1783         _manualStream->startStreamWithResponse([[self dataSource] response]);
1784     }
1786     if (_manualStream->plugin())
1787         _manualStream->didReceiveData(0, static_cast<const char *>([data bytes]), [data length]);
1790 - (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
1792     ASSERT(_loadManually);
1794     _error = error;
1795     
1796     if (![self isStarted]) {
1797         return;
1798     }
1800     _manualStream->destroyStreamWithError(error);
1803 - (void)pluginViewFinishedLoading:(NSView *)pluginView 
1805     ASSERT(_loadManually);
1806     ASSERT(_manualStream);
1807     
1808     if ([self isStarted])
1809         _manualStream->didFinishLoading(0);
1812 #pragma mark NSTextInput implementation
1814 - (NSTextInputContext *)inputContext
1816 #ifndef NP_NO_CARBON
1817     if (![self isStarted] || eventModel == NPEventModelCarbon)
1818         return nil;
1819 #endif
1821     return [super inputContext];
1824 - (BOOL)hasMarkedText
1826     ASSERT(eventModel == NPEventModelCocoa);
1827     ASSERT([self isStarted]);
1828     
1829     if (textInputFuncs && textInputFuncs->hasMarkedText)
1830         return textInputFuncs->hasMarkedText(plugin);
1831     
1832     return NO;
1835 - (void)insertText:(id)aString
1837     ASSERT(eventModel == NPEventModelCocoa);
1838     ASSERT([self isStarted]);
1839     
1840     if (textInputFuncs && textInputFuncs->insertText)
1841         textInputFuncs->insertText(plugin, aString);
1844 - (NSRange)markedRange
1846     ASSERT(eventModel == NPEventModelCocoa);
1847     ASSERT([self isStarted]);
1849     if (textInputFuncs && textInputFuncs->markedRange)
1850         return textInputFuncs->markedRange(plugin);
1851     
1852     return NSMakeRange(NSNotFound, 0);
1855 - (NSRange)selectedRange
1857     ASSERT(eventModel == NPEventModelCocoa);
1858     ASSERT([self isStarted]);
1860     if (textInputFuncs && textInputFuncs->selectedRange)
1861         return textInputFuncs->selectedRange(plugin);
1863     return NSMakeRange(NSNotFound, 0);
1864 }    
1866 - (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange
1868     ASSERT(eventModel == NPEventModelCocoa);
1869     ASSERT([self isStarted]);
1871     if (textInputFuncs && textInputFuncs->setMarkedText)
1872         textInputFuncs->setMarkedText(plugin, aString, selRange);
1875 - (void)unmarkText
1877     ASSERT(eventModel == NPEventModelCocoa);
1878     ASSERT([self isStarted]);
1879     
1880     if (textInputFuncs && textInputFuncs->unmarkText)
1881         textInputFuncs->unmarkText(plugin);
1884 - (NSArray *)validAttributesForMarkedText
1886     ASSERT(eventModel == NPEventModelCocoa);
1887     ASSERT([self isStarted]);
1888         
1889     if (textInputFuncs && textInputFuncs->validAttributesForMarkedText)
1890         return textInputFuncs->validAttributesForMarkedText(plugin);
1891     
1892     return [NSArray array];
1895 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange
1897     ASSERT(eventModel == NPEventModelCocoa);
1898     ASSERT([self isStarted]);
1899     
1900     if (textInputFuncs && textInputFuncs->attributedSubstringFromRange)
1901         return textInputFuncs->attributedSubstringFromRange(plugin, theRange);
1903     return nil;
1906 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
1908     ASSERT(eventModel == NPEventModelCocoa);
1909     ASSERT([self isStarted]);
1911     if (textInputFuncs && textInputFuncs->characterIndexForPoint) {
1912         // Convert the point to window coordinates
1913         NSPoint point = [[self window] convertScreenToBase:thePoint];
1914         
1915         // And view coordinates
1916         point = [self convertPoint:point fromView:nil];
1917         
1918         return textInputFuncs->characterIndexForPoint(plugin, point);
1919     }        
1921     return NSNotFound;
1924 - (void)doCommandBySelector:(SEL)aSelector
1926     ASSERT(eventModel == NPEventModelCocoa);
1927     ASSERT([self isStarted]);
1929     if (textInputFuncs && textInputFuncs->doCommandBySelector)
1930         textInputFuncs->doCommandBySelector(plugin, aSelector);
1933 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
1935     ASSERT(eventModel == NPEventModelCocoa);
1936     ASSERT([self isStarted]);
1938     if (textInputFuncs && textInputFuncs->firstRectForCharacterRange) {
1939         NSRect rect = textInputFuncs->firstRectForCharacterRange(plugin, theRange);
1940         
1941         // Convert the rect to window coordinates
1942         rect = [self convertRect:rect toView:nil];
1943         
1944         // Convert the rect location to screen coordinates
1945         rect.origin = [[self window] convertBaseToScreen:rect.origin];
1946         
1947         return rect;
1948     }
1950     return NSZeroRect;
1953 // test for 10.4 because of <rdar://problem/4243463>
1954 #ifdef BUILDING_ON_TIGER
1955 - (long)conversationIdentifier
1957     return (long)self;
1959 #else
1960 - (NSInteger)conversationIdentifier
1962     return (NSInteger)self;
1964 #endif
1966 @end
1968 @implementation WebBaseNetscapePluginView (WebNPPCallbacks)
1970 - (NSMutableURLRequest *)requestWithURLCString:(const char *)URLCString
1972     if (!URLCString)
1973         return nil;
1974     
1975     CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, URLCString, kCFStringEncodingISOLatin1);
1976     ASSERT(string); // All strings should be representable in ISO Latin 1
1977     
1978     NSString *URLString = [(NSString *)string _web_stringByStrippingReturnCharacters];
1979     NSURL *URL = [NSURL _web_URLWithDataAsString:URLString relativeToURL:_baseURL.get()];
1980     CFRelease(string);
1981     if (!URL)
1982         return nil;
1984     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
1985     Frame* frame = core([self webFrame]);
1986     if (!frame)
1987         return nil;
1988     [request _web_setHTTPReferrer:frame->loader()->outgoingReferrer()];
1989     return request;
1992 - (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest
1994     // FIXME: Is this isStarted check needed here? evaluateJavaScriptPluginRequest should not be called
1995     // if we are stopped since this method is called after a delay and we call 
1996     // cancelPreviousPerformRequestsWithTarget inside of stop.
1997     if (!isStarted) {
1998         return;
1999     }
2000     
2001     NSURL *URL = [[JSPluginRequest request] URL];
2002     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
2003     ASSERT(JSString);
2004     
2005     NSString *result = [[self webFrame] _stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]];
2006     
2007     // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop.
2008     if (!isStarted) {
2009         return;
2010     }
2011         
2012     if ([JSPluginRequest frameName] != nil) {
2013         // FIXME: If the result is a string, we probably want to put that string into the frame.
2014         if ([JSPluginRequest sendNotification]) {
2015             [self willCallPlugInFunction];
2016             {
2017                 JSC::JSLock::DropAllLocks dropAllLocks(false);
2018                 NPP_URLNotify(plugin, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]);
2019             }
2020             [self didCallPlugInFunction];
2021         }
2022     } else if ([result length] > 0) {
2023         // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does.
2024         NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding];
2025         
2026         RefPtr<WebNetscapePluginStream> stream = WebNetscapePluginStream::create([NSURLRequest requestWithURL:URL], plugin, [JSPluginRequest sendNotification], [JSPluginRequest notifyData]);
2027         
2028         RetainPtr<NSURLResponse> response(AdoptNS, [[NSURLResponse alloc] initWithURL:URL 
2029                                                                              MIMEType:@"text/plain" 
2030                                                                 expectedContentLength:[JSData length]
2031                                                                      textEncodingName:nil]);
2032         
2033         stream->startStreamWithResponse(response.get());
2034         stream->didReceiveData(0, static_cast<const char*>([JSData bytes]), [JSData length]);
2035         stream->didFinishLoading(0);
2036     }
2039 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
2041     ASSERT(isStarted);
2042     
2043     WebPluginRequest *pluginRequest = [_pendingFrameLoads.get() objectForKey:webFrame];
2044     ASSERT(pluginRequest != nil);
2045     ASSERT([pluginRequest sendNotification]);
2046         
2047     [self willCallPlugInFunction];
2048     {
2049         JSC::JSLock::DropAllLocks dropAllLocks(false);
2050         NPP_URLNotify(plugin, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]);
2051     }
2052     [self didCallPlugInFunction];
2053     
2054     [_pendingFrameLoads.get() removeObjectForKey:webFrame];
2055     [webFrame _setInternalLoadDelegate:nil];
2058 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
2060     NPReason reason = NPRES_DONE;
2061     if (error != nil)
2062         reason = WebNetscapePluginStream::reasonForError(error);
2063     [self webFrame:webFrame didFinishLoadWithReason:reason];
2066 - (void)loadPluginRequest:(WebPluginRequest *)pluginRequest
2068     NSURLRequest *request = [pluginRequest request];
2069     NSString *frameName = [pluginRequest frameName];
2070     WebFrame *frame = nil;
2071     
2072     NSURL *URL = [request URL];
2073     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
2074     
2075     ASSERT(frameName || JSString);
2076     
2077     if (frameName) {
2078         // FIXME - need to get rid of this window creation which
2079         // bypasses normal targeted link handling
2080         frame = kit(core([self webFrame])->loader()->findFrameForNavigation(frameName));
2081         if (frame == nil) {
2082             WebView *currentWebView = [self webView];
2083             NSDictionary *features = [[NSDictionary alloc] init];
2084             WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView
2085                                                         createWebViewWithRequest:nil
2086                                                                   windowFeatures:features];
2087             [features release];
2089             if (!newWebView) {
2090                 if ([pluginRequest sendNotification]) {
2091                     [self willCallPlugInFunction];
2092                     {
2093                         JSC::JSLock::DropAllLocks dropAllLocks(false);
2094                         NPP_URLNotify(plugin, [[[pluginRequest request] URL] _web_URLCString], NPERR_GENERIC_ERROR, [pluginRequest notifyData]);
2095                     }
2096                     [self didCallPlugInFunction];
2097                 }
2098                 return;
2099             }
2100             
2101             frame = [newWebView mainFrame];
2102             core(frame)->tree()->setName(frameName);
2103             [[newWebView _UIDelegateForwarder] webViewShow:newWebView];
2104         }
2105     }
2107     if (JSString) {
2108         ASSERT(frame == nil || [self webFrame] == frame);
2109         [self evaluateJavaScriptPluginRequest:pluginRequest];
2110     } else {
2111         [frame loadRequest:request];
2112         if ([pluginRequest sendNotification]) {
2113             // Check if another plug-in view or even this view is waiting for the frame to load.
2114             // If it is, tell it that the load was cancelled because it will be anyway.
2115             WebBaseNetscapePluginView *view = [frame _internalLoadDelegate];
2116             if (view != nil) {
2117                 ASSERT([view isKindOfClass:[WebBaseNetscapePluginView class]]);
2118                 [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK];
2119             }
2120             [_pendingFrameLoads.get() _webkit_setObject:pluginRequest forUncopiedKey:frame];
2121             [frame _setInternalLoadDelegate:self];
2122         }
2123     }
2126 - (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification
2128     NSURL *URL = [request URL];
2130     if (!URL) 
2131         return NPERR_INVALID_URL;
2133     // Don't allow requests to be loaded when the document loader is stopping all loaders.
2134     if ([[self dataSource] _documentLoader]->isStopping())
2135         return NPERR_GENERIC_ERROR;
2136     
2137     NSString *target = nil;
2138     if (cTarget) {
2139         // Find the frame given the target string.
2140         target = [NSString stringWithCString:cTarget encoding:NSISOLatin1StringEncoding];
2141     }
2142     WebFrame *frame = [self webFrame];
2144     // don't let a plugin start any loads if it is no longer part of a document that is being 
2145     // displayed unless the loads are in the same frame as the plugin.
2146     if ([[self dataSource] _documentLoader] != core([self webFrame])->loader()->activeDocumentLoader() &&
2147         (!cTarget || [frame findFrameNamed:target] != frame)) {
2148         return NPERR_GENERIC_ERROR; 
2149     }
2150     
2151     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
2152     if (JSString != nil) {
2153         if (![[[self webView] preferences] isJavaScriptEnabled]) {
2154             // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does.
2155             return NPERR_GENERIC_ERROR;
2156         } else if (cTarget == NULL && _mode == NP_FULL) {
2157             // Don't allow a JavaScript request from a standalone plug-in that is self-targetted
2158             // because this can cause the user to be redirected to a blank page (3424039).
2159             return NPERR_INVALID_PARAM;
2160         }
2161     } else {
2162         if (!FrameLoader::canLoad(URL, String(), core([self webFrame])->document()))
2163             return NPERR_GENERIC_ERROR;
2164     }
2165         
2166     if (cTarget || JSString) {
2167         // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't
2168         // want to potentially kill the plug-in inside of its URL request.
2169         
2170         if (JSString && target && [frame findFrameNamed:target] != frame) {
2171             // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
2172             return NPERR_INVALID_PARAM;
2173         }
2174         
2175         bool currentEventIsUserGesture = false;
2176         if (_eventHandler)
2177             currentEventIsUserGesture = _eventHandler->currentEventIsUserGesture();
2178         
2179         WebPluginRequest *pluginRequest = [[WebPluginRequest alloc] initWithRequest:request 
2180                                                                           frameName:target
2181                                                                          notifyData:notifyData 
2182                                                                    sendNotification:sendNotification
2183                                                             didStartFromUserGesture:currentEventIsUserGesture];
2184         [self performSelector:@selector(loadPluginRequest:) withObject:pluginRequest afterDelay:0];
2185         [pluginRequest release];
2186     } else {
2187         RefPtr<WebNetscapePluginStream> stream = WebNetscapePluginStream::create(request, plugin, sendNotification, notifyData);
2189         streams.add(stream.get());
2190         stream->start();
2191     }
2192     
2193     return NPERR_NO_ERROR;
2196 -(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData
2198     LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget);
2200     NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
2201     return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES];
2204 -(NPError)getURL:(const char *)URLCString target:(const char *)cTarget
2206     LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget);
2208     NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
2209     return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO];
2212 - (NPError)_postURL:(const char *)URLCString
2213              target:(const char *)target
2214                 len:(UInt32)len
2215                 buf:(const char *)buf
2216                file:(NPBool)file
2217          notifyData:(void *)notifyData
2218    sendNotification:(BOOL)sendNotification
2219        allowHeaders:(BOOL)allowHeaders
2221     if (!URLCString || !len || !buf) {
2222         return NPERR_INVALID_PARAM;
2223     }
2224     
2225     NSData *postData = nil;
2227     if (file) {
2228         // If we're posting a file, buf is either a file URL or a path to the file.
2229         NSString *bufString = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingWindowsLatin1);
2230         if (!bufString) {
2231             return NPERR_INVALID_PARAM;
2232         }
2233         NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString];
2234         NSString *path;
2235         if ([fileURL isFileURL]) {
2236             path = [fileURL path];
2237         } else {
2238             path = bufString;
2239         }
2240         postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]];
2241         CFRelease(bufString);
2242         if (!postData) {
2243             return NPERR_FILE_NOT_FOUND;
2244         }
2245     } else {
2246         postData = [NSData dataWithBytes:buf length:len];
2247     }
2249     if ([postData length] == 0) {
2250         return NPERR_INVALID_PARAM;
2251     }
2253     NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
2254     [request setHTTPMethod:@"POST"];
2255     
2256     if (allowHeaders) {
2257         if ([postData _web_startsWithBlankLine]) {
2258             postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)];
2259         } else {
2260             NSInteger location = [postData _web_locationAfterFirstBlankLine];
2261             if (location != NSNotFound) {
2262                 // If the blank line is somewhere in the middle of postData, everything before is the header.
2263                 NSData *headerData = [postData subdataWithRange:NSMakeRange(0, location)];
2264                 NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields];
2265                 unsigned dataLength = [postData length] - location;
2267                 // Sometimes plugins like to set Content-Length themselves when they post,
2268                 // but WebFoundation does not like that. So we will remove the header
2269                 // and instead truncate the data to the requested length.
2270                 NSString *contentLength = [header objectForKey:@"Content-Length"];
2272                 if (contentLength != nil)
2273                     dataLength = MIN((unsigned)[contentLength intValue], dataLength);
2274                 [header removeObjectForKey:@"Content-Length"];
2276                 if ([header count] > 0) {
2277                     [request setAllHTTPHeaderFields:header];
2278                 }
2279                 // Everything after the blank line is the actual content of the POST.
2280                 postData = [postData subdataWithRange:NSMakeRange(location, dataLength)];
2282             }
2283         }
2284         if ([postData length] == 0) {
2285             return NPERR_INVALID_PARAM;
2286         }
2287     }
2289     // Plug-ins expect to receive uncached data when doing a POST (3347134).
2290     [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
2291     [request setHTTPBody:postData];
2292     
2293     return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification];
2296 - (NPError)postURLNotify:(const char *)URLCString
2297                   target:(const char *)target
2298                      len:(UInt32)len
2299                      buf:(const char *)buf
2300                     file:(NPBool)file
2301               notifyData:(void *)notifyData
2303     LOG(Plugins, "NPN_PostURLNotify: %s", URLCString);
2304     return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:notifyData sendNotification:YES allowHeaders:YES];
2307 -(NPError)postURL:(const char *)URLCString
2308            target:(const char *)target
2309               len:(UInt32)len
2310               buf:(const char *)buf
2311              file:(NPBool)file
2313     LOG(Plugins, "NPN_PostURL: %s", URLCString);        
2314     // As documented, only allow headers to be specified via NPP_PostURL when using a file.
2315     return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:NULL sendNotification:NO allowHeaders:file];
2318 -(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream
2320     LOG(Plugins, "NPN_NewStream");
2321     return NPERR_GENERIC_ERROR;
2324 -(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer
2326     LOG(Plugins, "NPN_Write");
2327     return NPERR_GENERIC_ERROR;
2330 -(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason
2332     LOG(Plugins, "NPN_DestroyStream");
2333     // This function does a sanity check to ensure that the NPStream provided actually
2334     // belongs to the plug-in that provided it, which fixes a crash in the DivX 
2335     // plug-in: <rdar://problem/5093862> | http://bugs.webkit.org/show_bug.cgi?id=13203
2336     if (!stream || WebNetscapePluginStream::ownerForStream(stream) != plugin) {
2337         LOG(Plugins, "Invalid NPStream passed to NPN_DestroyStream: %p", stream);
2338         return NPERR_INVALID_INSTANCE_ERROR;
2339     }
2340     
2341     WebNetscapePluginStream* browserStream = static_cast<WebNetscapePluginStream*>(stream->ndata);
2342     browserStream->cancelLoadAndDestroyStreamWithError(browserStream->errorForReason(reason));
2343     
2344     return NPERR_NO_ERROR;
2347 - (const char *)userAgent
2349     return [[[self webView] userAgentForURL:_baseURL.get()] UTF8String];
2352 -(void)status:(const char *)message
2353 {    
2354     if (!message) {
2355         LOG_ERROR("NPN_Status passed a NULL status message");
2356         return;
2357     }
2359     CFStringRef status = CFStringCreateWithCString(NULL, message, kCFStringEncodingUTF8);
2360     if (!status) {
2361         LOG_ERROR("NPN_Status: the message was not valid UTF-8");
2362         return;
2363     }
2364     
2365     LOG(Plugins, "NPN_Status: %@", status);
2366     WebView *wv = [self webView];
2367     [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status];
2368     CFRelease(status);
2371 -(void)invalidateRect:(NPRect *)invalidRect
2373     LOG(Plugins, "NPN_InvalidateRect");
2374     [self setNeedsDisplayInRect:NSMakeRect(invalidRect->left, invalidRect->top,
2375         (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)];
2378 -(BOOL)isOpaque
2380     return YES;
2383 - (void)invalidateRegion:(NPRegion)invalidRegion
2385     LOG(Plugins, "NPN_InvalidateRegion");
2386     NSRect invalidRect = NSZeroRect;
2387     switch (drawingModel) {
2388 #ifndef NP_NO_QUICKDRAW
2389         case NPDrawingModelQuickDraw:
2390         {
2391             ::Rect qdRect;
2392             GetRegionBounds((NPQDRegion)invalidRegion, &qdRect);
2393             invalidRect = NSMakeRect(qdRect.left, qdRect.top, qdRect.right - qdRect.left, qdRect.bottom - qdRect.top);
2394         }
2395         break;
2396 #endif /* NP_NO_QUICKDRAW */
2397         
2398         case NPDrawingModelCoreGraphics:
2399         {
2400             CGRect cgRect = CGPathGetBoundingBox((NPCGRegion)invalidRegion);
2401             invalidRect = *(NSRect*)&cgRect;
2402             break;
2403         }
2404         default:
2405             ASSERT_NOT_REACHED();
2406         break;
2407     }
2408     
2409     [self setNeedsDisplayInRect:invalidRect];
2412 -(void)forceRedraw
2414     LOG(Plugins, "forceRedraw");
2415     [self setNeedsDisplay:YES];
2416     [[self window] displayIfNeeded];
2419 static NPBrowserTextInputFuncs *browserTextInputFuncs()
2421     static NPBrowserTextInputFuncs inputFuncs = {
2422         0,
2423         sizeof(NPBrowserTextInputFuncs),
2424         NPN_MarkedTextAbandoned,
2425         NPN_MarkedTextSelectionChanged
2426     };
2427     
2428     return &inputFuncs;
2431 - (NPError)getVariable:(NPNVariable)variable value:(void *)value
2433     switch (variable) {
2434         case NPNVWindowNPObject:
2435         {
2436             Frame* frame = core([self webFrame]);
2437             NPObject* windowScriptObject = frame ? frame->script()->windowScriptNPObject() : 0;
2439             // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
2440             if (windowScriptObject)
2441                 _NPN_RetainObject(windowScriptObject);
2442             
2443             void **v = (void **)value;
2444             *v = windowScriptObject;
2446             return NPERR_NO_ERROR;
2447         }
2449         case NPNVPluginElementNPObject:
2450         {
2451             if (!_element)
2452                 return NPERR_GENERIC_ERROR;
2453             
2454             NPObject *plugInScriptObject = (NPObject *)[_element.get() _NPObject];
2456             // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
2457             if (plugInScriptObject)
2458                 _NPN_RetainObject(plugInScriptObject);
2460             void **v = (void **)value;
2461             *v = plugInScriptObject;
2463             return NPERR_NO_ERROR;
2464         }
2465         
2466         case NPNVpluginDrawingModel:
2467         {
2468             *(NPDrawingModel *)value = drawingModel;
2469             return NPERR_NO_ERROR;
2470         }
2472 #ifndef NP_NO_QUICKDRAW
2473         case NPNVsupportsQuickDrawBool:
2474         {
2475             *(NPBool *)value = TRUE;
2476             return NPERR_NO_ERROR;
2477         }
2478 #endif /* NP_NO_QUICKDRAW */
2479         
2480         case NPNVsupportsCoreGraphicsBool:
2481         {
2482             *(NPBool *)value = TRUE;
2483             return NPERR_NO_ERROR;
2484         }
2486         case NPNVsupportsOpenGLBool:
2487         {
2488             *(NPBool *)value = FALSE;
2489             return NPERR_NO_ERROR;
2490         }
2491         
2492         case NPNVsupportsCoreAnimationBool:
2493         {
2494 #ifdef BUILDING_ON_TIGER
2495             *(NPBool *)value = FALSE;
2496 #else
2497             *(NPBool *)value = TRUE;
2498 #endif
2499             return NPERR_NO_ERROR;
2500         }
2501             
2502 #ifndef NP_NO_CARBON
2503         case NPNVsupportsCarbonBool:
2504         {
2505             *(NPBool *)value = TRUE;
2506             return NPERR_NO_ERROR;
2507         }
2508 #endif /* NP_NO_CARBON */
2509             
2510         case NPNVsupportsCocoaBool:
2511         {
2512             *(NPBool *)value = TRUE;
2513             return NPERR_NO_ERROR;
2514         }
2515             
2516         case NPNVbrowserTextInputFuncs:
2517         {
2518             if (eventModel == NPEventModelCocoa) {
2519                 *(NPBrowserTextInputFuncs **)value = browserTextInputFuncs();
2520                 return NPERR_NO_ERROR;
2521             }
2522         }
2523         default:
2524             break;
2525     }
2527     return NPERR_GENERIC_ERROR;
2530 - (NPError)setVariable:(NPPVariable)variable value:(void *)value
2532     switch (variable) {
2533         case NPPVpluginDrawingModel:
2534         {
2535             // Can only set drawing model inside NPP_New()
2536             if (self != [[self class] currentPluginView])
2537                 return NPERR_GENERIC_ERROR;
2538             
2539             // Check for valid, supported drawing model
2540             NPDrawingModel newDrawingModel = (NPDrawingModel)(uintptr_t)value;
2541             switch (newDrawingModel) {
2542                 // Supported drawing models:
2543 #ifndef NP_NO_QUICKDRAW
2544                 case NPDrawingModelQuickDraw:
2545 #endif
2546                 case NPDrawingModelCoreGraphics:
2547 #ifndef BUILDING_ON_TIGER
2548                 case NPDrawingModelCoreAnimation:
2549 #endif
2550                     drawingModel = newDrawingModel;
2551                     return NPERR_NO_ERROR;
2552                     
2554                 // Unsupported (or unknown) drawing models:
2555                 default:
2556                     LOG(Plugins, "Plugin %@ uses unsupported drawing model: %d", _eventHandler.get(), drawingModel);
2557                     return NPERR_GENERIC_ERROR;
2558             }
2559         }
2560         
2561         case NPPVpluginEventModel:
2562         {
2563             // Can only set event model inside NPP_New()
2564             if (self != [[self class] currentPluginView])
2565                 return NPERR_GENERIC_ERROR;
2566             
2567             // Check for valid, supported event model
2568             NPEventModel newEventModel = (NPEventModel)(uintptr_t)value;
2569             switch (newEventModel) {
2570                 // Supported event models:
2571 #ifndef NP_NO_CARBON
2572                 case NPEventModelCarbon:
2573 #endif
2574                 case NPEventModelCocoa:
2575                     eventModel = newEventModel;
2576                     return NPERR_NO_ERROR;
2577                     
2578                     // Unsupported (or unknown) event models:
2579                 default:
2580                     LOG(Plugins, "Plugin %@ uses unsupported event model: %d", _eventHandler.get(), eventModel);
2581                     return NPERR_GENERIC_ERROR;
2582             }
2583         }
2584             
2585         default:
2586             return NPERR_GENERIC_ERROR;
2587     }
2590 - (uint32)scheduleTimerWithInterval:(uint32)interval repeat:(NPBool)repeat timerFunc:(void (*)(NPP npp, uint32 timerID))timerFunc
2592     if (!timerFunc)
2593         return 0;
2594     
2595     if (!timers)
2596         timers = new HashMap<uint32, PluginTimer*>;
2597     
2598     uint32 timerID = ++currentTimerID;
2599     
2600     PluginTimer* timer = new PluginTimer(plugin, timerID, interval, repeat, timerFunc);
2601     timers->set(timerID, timer);
2603     if (shouldFireTimers)
2604         timer->start(isCompletelyObscured);
2605     
2606     return 0;
2609 - (void)unscheduleTimer:(uint32)timerID
2611     if (!timers)
2612         return;
2613     
2614     if (PluginTimer* timer = timers->take(timerID))
2615         delete timer;
2618 - (NPError)popUpContextMenu:(NPMenu *)menu
2620     NSEvent *currentEvent = [NSApp currentEvent];
2621     
2622     // NPN_PopUpContextMenu must be called from within the plug-in's NPP_HandleEvent.
2623     if (!currentEvent)
2624         return NPERR_GENERIC_ERROR;
2625     
2626     [NSMenu popUpContextMenu:(NSMenu *)menu withEvent:currentEvent forView:self];
2627     return NPERR_NO_ERROR;
2630 @end
2632 @implementation WebPluginRequest
2634 - (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture
2636     [super init];
2637     _didStartFromUserGesture = currentEventIsUserGesture;
2638     _request = [request retain];
2639     _frameName = [frameName retain];
2640     _notifyData = notifyData;
2641     _sendNotification = sendNotification;
2642     return self;
2645 - (void)dealloc
2647     [_request release];
2648     [_frameName release];
2649     [super dealloc];
2652 - (NSURLRequest *)request
2654     return _request;
2657 - (NSString *)frameName
2659     return _frameName;
2662 - (BOOL)isCurrentEventUserGesture
2664     return _didStartFromUserGesture;
2667 - (BOOL)sendNotification
2669     return _sendNotification;
2672 - (void *)notifyData
2674     return _notifyData;
2677 @end
2679 @implementation WebBaseNetscapePluginView (Internal)
2681 - (NPError)_createPlugin
2683     plugin = (NPP)calloc(1, sizeof(NPP_t));
2684     plugin->ndata = self;
2686     ASSERT(NPP_New);
2688     // NPN_New(), which creates the plug-in instance, should never be called while calling a plug-in function for that instance.
2689     ASSERT(pluginFunctionCallDepth == 0);
2691     Frame* frame = core([self webFrame]);
2692     if (!frame)
2693         return NPERR_GENERIC_ERROR;
2694     Page* page = frame->page();
2695     if (!page)
2696         return NPERR_GENERIC_ERROR;
2697     
2698     bool wasDeferring = page->defersLoading();
2699     if (!wasDeferring)
2700         page->setDefersLoading(true);
2701     
2702     PluginMainThreadScheduler::scheduler().registerPlugin(plugin);
2703     
2704     [[self class] setCurrentPluginView:self];
2705     NPError npErr = NPP_New((char *)[_MIMEType.get() cString], plugin, _mode, argsCount, cAttributes, cValues, NULL);
2706     [[self class] setCurrentPluginView:nil];
2707     
2708     if (!wasDeferring)
2709         page->setDefersLoading(false);
2711     LOG(Plugins, "NPP_New: %d", npErr);
2712     return npErr;
2715 - (void)_destroyPlugin
2717     PluginMainThreadScheduler::scheduler().unregisterPlugin(plugin);
2718     
2719     NPError npErr;
2720     npErr = NPP_Destroy(plugin, NULL);
2721     LOG(Plugins, "NPP_Destroy: %d", npErr);
2722     
2723     if (Frame* frame = core([self webFrame]))
2724         frame->script()->cleanupScriptObjectsForPlugin(self);
2725         
2726     free(plugin);
2727     plugin = NULL;
2730 - (void)_viewHasMoved
2732     // All of the work this method does may safely be skipped if the view is not in a window.  When the view
2733     // is moved back into a window, everything should be set up correctly.
2734     if (![self window])
2735         return;
2737     if (isDrawingModelQuickDraw(drawingModel))
2738         [self tellQuickTimeToChill];
2740     if (drawingModel == NPDrawingModelCoreGraphics || isDrawingModelQuickDraw(drawingModel))
2741         [self updateAndSetWindow];
2742     
2743     [self resetTrackingRect];
2744     
2745     // Check to see if the plugin view is completely obscured (scrolled out of view, for example).
2746     // For performance reasons, we send null events at a lower rate to plugins which are obscured.
2747     BOOL oldIsObscured = isCompletelyObscured;
2748     isCompletelyObscured = NSIsEmptyRect([self visibleRect]);
2749     if (isCompletelyObscured != oldIsObscured)
2750         [self restartTimers];
2753 - (NSBitmapImageRep *)_printedPluginBitmap
2755 #ifdef NP_NO_QUICKDRAW
2756     return nil;
2757 #else
2758     // Cannot print plugins that do not implement NPP_Print
2759     if (!NPP_Print)
2760         return nil;
2762     // This NSBitmapImageRep will share its bitmap buffer with a GWorld that the plugin will draw into.
2763     // The bitmap is created in 32-bits-per-pixel ARGB format, which is the default GWorld pixel format.
2764     NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
2765                                                          pixelsWide:window.width
2766                                                          pixelsHigh:window.height
2767                                                          bitsPerSample:8
2768                                                          samplesPerPixel:4
2769                                                          hasAlpha:YES
2770                                                          isPlanar:NO
2771                                                          colorSpaceName:NSDeviceRGBColorSpace
2772                                                          bitmapFormat:NSAlphaFirstBitmapFormat
2773                                                          bytesPerRow:0
2774                                                          bitsPerPixel:0] autorelease];
2775     ASSERT(bitmap);
2776     
2777     // Create a GWorld with the same underlying buffer into which the plugin can draw
2778     ::Rect printGWorldBounds;
2779     SetRect(&printGWorldBounds, 0, 0, window.width, window.height);
2780     GWorldPtr printGWorld;
2781     if (NewGWorldFromPtr(&printGWorld,
2782                          k32ARGBPixelFormat,
2783                          &printGWorldBounds,
2784                          NULL,
2785                          NULL,
2786                          0,
2787                          (Ptr)[bitmap bitmapData],
2788                          [bitmap bytesPerRow]) != noErr) {
2789         LOG_ERROR("Could not create GWorld for printing");
2790         return nil;
2791     }
2792     
2793     /// Create NPWindow for the GWorld
2794     NPWindow printNPWindow;
2795     printNPWindow.window = &printGWorld; // Normally this is an NP_Port, but when printing it is the actual CGrafPtr
2796     printNPWindow.x = 0;
2797     printNPWindow.y = 0;
2798     printNPWindow.width = window.width;
2799     printNPWindow.height = window.height;
2800     printNPWindow.clipRect.top = 0;
2801     printNPWindow.clipRect.left = 0;
2802     printNPWindow.clipRect.right = window.width;
2803     printNPWindow.clipRect.bottom = window.height;
2804     printNPWindow.type = NPWindowTypeDrawable; // Offscreen graphics port as opposed to a proper window
2805     
2806     // Create embed-mode NPPrint
2807     NPPrint npPrint;
2808     npPrint.mode = NP_EMBED;
2809     npPrint.print.embedPrint.window = printNPWindow;
2810     npPrint.print.embedPrint.platformPrint = printGWorld;
2811     
2812     // Tell the plugin to print into the GWorld
2813     [self willCallPlugInFunction];
2814     {
2815         JSC::JSLock::DropAllLocks dropAllLocks(false);
2816         NPP_Print(plugin, &npPrint);
2817     }
2818     [self didCallPlugInFunction];
2820     // Don't need the GWorld anymore
2821     DisposeGWorld(printGWorld);
2822         
2823     return bitmap;
2824 #endif
2827 - (void)_redeliverStream
2829     if ([self dataSource] && [self isStarted]) {
2830         // Deliver what has not been passed to the plug-in up to this point.
2831         if (_dataLengthReceived > 0) {
2832             NSData *data = [[[self dataSource] data] subdataWithRange:NSMakeRange(0, _dataLengthReceived)];
2833             _dataLengthReceived = 0;
2834             [self pluginView:self receivedData:data];
2835             if (![[self dataSource] isLoading]) {
2836                 if (_error)
2837                     [self pluginView:self receivedError:_error.get()];
2838                 else
2839                     [self pluginViewFinishedLoading:self];
2840             }
2841         }
2842     }
2845 @end
2847 @implementation NSData (PluginExtras)
2849 - (BOOL)_web_startsWithBlankLine
2851     return [self length] > 0 && ((const char *)[self bytes])[0] == '\n';
2855 - (NSInteger)_web_locationAfterFirstBlankLine
2857     const char *bytes = (const char *)[self bytes];
2858     unsigned length = [self length];
2859     
2860     unsigned i;
2861     for (i = 0; i < length - 4; i++) {
2862         
2863         //  Support for Acrobat. It sends "\n\n".
2864         if (bytes[i] == '\n' && bytes[i+1] == '\n') {
2865             return i+2;
2866         }
2867         
2868         // Returns the position after 2 CRLF's or 1 CRLF if it is the first line.
2869         if (bytes[i] == '\r' && bytes[i+1] == '\n') {
2870             i += 2;
2871             if (i == 2) {
2872                 return i;
2873             } else if (bytes[i] == '\n') {
2874                 // Support for Director. It sends "\r\n\n" (3880387).
2875                 return i+1;
2876             } else if (bytes[i] == '\r' && bytes[i+1] == '\n') {
2877                 // Support for Flash. It sends "\r\n\r\n" (3758113).
2878                 return i+2;
2879             }
2880         }
2881     }
2882     return NSNotFound;
2885 @end
2886 #endif