7794b070d6190ffb8d393be28a18132b6a474817
[WebKit-https.git] / WebKit / mac / Plugins / WebBaseNetscapePluginView.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #if ENABLE(NETSCAPE_PLUGIN_API)
30
31 #import "WebBaseNetscapePluginView.h"
32
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 "WebNetscapePluginStream.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 <JavaScriptCore/Assertions.h>
56 #import <JavaScriptCore/JSLock.h>
57 #import <WebCore/npruntime_impl.h>
58 #import <WebCore/Document.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/SoftLinking.h> 
65 #import <WebCore/WebCoreObjCExtras.h>
66 #import <WebKit/DOMPrivate.h>
67 #import <WebKit/WebUIDelegate.h>
68 #import <objc/objc-runtime.h>
69
70 using namespace WebCore;
71
72 #define LoginWindowDidSwitchFromUserNotification    @"WebLoginWindowDidSwitchFromUserNotification"
73 #define LoginWindowDidSwitchToUserNotification      @"WebLoginWindowDidSwitchToUserNotification"
74
75 SOFT_LINK_FRAMEWORK(OpenGL)
76 SOFT_LINK_FRAMEWORK(AGL)
77
78 SOFT_LINK(OpenGL, CGLGetOffScreen, CGLError, (CGLContextObj ctx, GLsizei *width, GLsizei *height, GLint *rowbytes, void **baseaddr), (ctx, width, height, rowbytes, baseaddr))
79 SOFT_LINK(OpenGL, CGLSetOffScreen, CGLError, (CGLContextObj ctx, GLsizei width, GLsizei height, GLint rowbytes, void *baseaddr), (ctx, width, height, rowbytes, baseaddr))
80 SOFT_LINK(OpenGL, glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height), (x, y, width, height))
81 SOFT_LINK(AGL, aglCreateContext, AGLContext, (AGLPixelFormat pix, AGLContext share), (pix, share))
82 SOFT_LINK(AGL, aglSetWindowRef, GLboolean, (AGLContext ctx, WindowRef window), (ctx, window))
83 SOFT_LINK(AGL, aglSetDrawable, GLboolean, (AGLContext ctx, AGLDrawable draw), (ctx, draw))
84 #ifndef BUILDING_ON_TIGER
85 SOFT_LINK(AGL, aglChoosePixelFormat, AGLPixelFormat, (const void *gdevs, GLint ndev, const GLint *attribs), (gdevs, ndev, attribs))
86 #else
87 SOFT_LINK(AGL, aglChoosePixelFormat, AGLPixelFormat, (const AGLDevice *gdevs, GLint ndev, const GLint *attribs), (gdevs, ndev, attribs))
88 #endif
89 SOFT_LINK(AGL, aglDestroyPixelFormat, void, (AGLPixelFormat pix), (pix))
90 SOFT_LINK(AGL, aglDestroyContext, GLboolean, (AGLContext ctx), (ctx))
91 SOFT_LINK(AGL, aglGetCGLContext, GLboolean, (AGLContext ctx, void **cgl_ctx), (ctx, cgl_ctx))
92 SOFT_LINK(AGL, aglGetCurrentContext, AGLContext, (void), ())
93 SOFT_LINK(AGL, aglSetCurrentContext, GLboolean, (AGLContext ctx), (ctx))
94 SOFT_LINK(AGL, aglGetError, GLenum, (void), ())
95 SOFT_LINK(AGL, aglUpdateContext, GLboolean, (AGLContext ctx), (ctx))
96 SOFT_LINK(AGL, aglErrorString, const GLubyte *, (GLenum code), (code))
97
98 @interface WebBaseNetscapePluginView (Internal)
99 - (void)_viewHasMoved;
100 - (NPError)_createPlugin;
101 - (void)_destroyPlugin;
102 - (NSBitmapImageRep *)_printedPluginBitmap;
103 - (BOOL)_createAGLContextIfNeeded;
104 - (BOOL)_createWindowedAGLContext;
105 - (BOOL)_createWindowlessAGLContext;
106 - (CGLContextObj)_cglContext;
107 - (BOOL)_getAGLOffscreenBuffer:(GLvoid **)outBuffer width:(GLsizei *)outWidth height:(GLsizei *)outHeight;
108 - (void)_destroyAGLContext;
109 - (void)_reshapeAGLWindow;
110 - (void)_hideAGLWindow;
111 - (NSImage *)_aglOffscreenImageForDrawingInRect:(NSRect)drawingInRect;
112 - (void)_redeliverStream;
113 @end
114
115 static WebBaseNetscapePluginView *currentPluginView = nil;
116
117 typedef struct OpaquePortState* PortState;
118
119 static const double ThrottledTimerInterval = 0.25;
120
121 class PluginTimer : public TimerBase {
122 public:
123     typedef void (*TimerFunc)(NPP npp, uint32 timerID);
124     
125     PluginTimer(NPP npp, uint32 timerID, uint32 interval, NPBool repeat, TimerFunc timerFunc)
126         : m_npp(npp)
127         , m_timerID(timerID)
128         , m_interval(interval)
129         , m_repeat(repeat)
130         , m_timerFunc(timerFunc)
131     {
132     }
133     
134     void start(bool throttle)
135     {
136         ASSERT(!isActive());
137         
138         double timeInterval = throttle ? ThrottledTimerInterval : m_interval / 1000.0;
139         if (m_repeat)
140             startRepeating(timeInterval);
141         else
142             startOneShot(timeInterval);
143     }
144
145 private:
146     virtual void fired() 
147     {
148         m_timerFunc(m_npp, m_timerID);
149         if (!m_repeat)
150             delete this;
151     }
152     
153     NPP m_npp;
154     uint32 m_timerID;
155     uint32 m_interval;
156     NPBool m_repeat;
157     TimerFunc m_timerFunc;
158 };
159
160 #ifndef NP_NO_QUICKDRAW
161
162 // QuickDraw is not available in 64-bit
163
164 typedef struct {
165     GrafPtr oldPort;
166     GDHandle oldDevice;
167     Point oldOrigin;
168     RgnHandle oldClipRegion;
169     RgnHandle oldVisibleRegion;
170     RgnHandle clipRegion;
171     BOOL forUpdate;
172 } PortState_QD;
173
174 #endif /* NP_NO_QUICKDRAW */
175
176 typedef struct {
177     CGContextRef context;
178 } PortState_CG;
179
180 typedef struct {
181     AGLContext oldContext;
182 } PortState_GL;
183
184 @interface WebPluginRequest : NSObject
185 {
186     NSURLRequest *_request;
187     NSString *_frameName;
188     void *_notifyData;
189     BOOL _didStartFromUserGesture;
190     BOOL _sendNotification;
191 }
192
193 - (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture;
194
195 - (NSURLRequest *)request;
196 - (NSString *)frameName;
197 - (void *)notifyData;
198 - (BOOL)isCurrentEventUserGesture;
199 - (BOOL)sendNotification;
200
201 @end
202
203 @interface NSData (WebPluginDataExtras)
204 - (BOOL)_web_startsWithBlankLine;
205 - (NSInteger)_web_locationAfterFirstBlankLine;
206 @end
207
208 @interface WebBaseNetscapePluginView (ForwardDeclarations)
209 - (void)setWindowIfNecessary;
210 - (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification;
211 @end
212
213 @implementation WebBaseNetscapePluginView
214
215 + (void)initialize
216 {
217 #ifndef BUILDING_ON_TIGER
218     WebCoreObjCFinalizeOnMainThread(self);
219 #endif
220     WKSendUserChangeNotifications();
221 }
222
223 #pragma mark EVENTS
224
225 - (BOOL)superviewsHaveSuperviews
226 {
227     NSView *contentView = [[self window] contentView];
228     NSView *view;
229     for (view = self; view != nil; view = [view superview]) { 
230         if (view == contentView) {
231             return YES;
232         }
233     }
234     return NO;
235 }
236
237 #ifndef NP_NO_QUICKDRAW
238
239 // The WindowRef created by -[NSWindow windowRef] has a QuickDraw GrafPort that covers 
240 // the entire window frame (or structure region to use the Carbon term) rather then just the window content.
241 // We can remove this when <rdar://problem/4201099> is fixed.
242 - (void)fixWindowPort
243 {
244     ASSERT(drawingModel == NPDrawingModelQuickDraw);
245     
246     NSWindow *currentWindow = [self currentWindow];
247     if ([currentWindow isKindOfClass:objc_getClass("NSCarbonWindow")])
248         return;
249     
250     float windowHeight = [currentWindow frame].size.height;
251     NSView *contentView = [currentWindow contentView];
252     NSRect contentRect = [contentView convertRect:[contentView frame] toView:nil]; // convert to window-relative coordinates
253     
254     CGrafPtr oldPort;
255     GetPort(&oldPort);    
256     SetPort(GetWindowPort((WindowRef)[currentWindow windowRef]));
257     
258     MovePortTo(static_cast<short>(contentRect.origin.x), /* Flip Y */ static_cast<short>(windowHeight - NSMaxY(contentRect)));
259     PortSize(static_cast<short>(contentRect.size.width), static_cast<short>(contentRect.size.height));
260     
261     SetPort(oldPort);
262 }
263
264 static UInt32 getQDPixelFormatForBitmapContext(CGContextRef context)
265 {
266     UInt32 byteOrder = CGBitmapContextGetBitmapInfo(context) & kCGBitmapByteOrderMask;
267     if (byteOrder == kCGBitmapByteOrderDefault)
268         switch (CGBitmapContextGetBitsPerPixel(context)) {
269             case 16:
270                 byteOrder = kCGBitmapByteOrder16Host;
271                 break;
272             case 32:
273                 byteOrder = kCGBitmapByteOrder32Host;
274                 break;
275         }
276     switch (byteOrder) {
277         case kCGBitmapByteOrder16Little:
278             return k16LE555PixelFormat;
279         case kCGBitmapByteOrder32Little:
280             return k32BGRAPixelFormat;
281         case kCGBitmapByteOrder16Big:
282             return k16BE555PixelFormat;
283         case kCGBitmapByteOrder32Big:
284             return k32ARGBPixelFormat;
285     }
286     ASSERT_NOT_REACHED();
287     return 0;
288 }
289
290 static inline void getNPRect(const CGRect& cgr, NPRect& npr)
291 {
292     npr.top = static_cast<uint16>(cgr.origin.y);
293     npr.left = static_cast<uint16>(cgr.origin.x);
294     npr.bottom = static_cast<uint16>(CGRectGetMaxY(cgr));
295     npr.right = static_cast<uint16>(CGRectGetMaxX(cgr));
296 }
297
298 #endif
299
300 static inline void getNPRect(const NSRect& nr, NPRect& npr)
301 {
302     npr.top = static_cast<uint16>(nr.origin.y);
303     npr.left = static_cast<uint16>(nr.origin.x);
304     npr.bottom = static_cast<uint16>(NSMaxY(nr));
305     npr.right = static_cast<uint16>(NSMaxX(nr));
306 }
307
308 - (NSRect)visibleRect
309 {
310     // WebCore may impose an additional clip (via CSS overflow or clip properties).  Fetch
311     // that clip now.    
312     return NSIntersectionRect([self convertRect:[element _windowClipRect] fromView:nil], [super visibleRect]);
313 }
314
315 - (PortState)saveAndSetNewPortStateForUpdate:(BOOL)forUpdate
316 {
317     ASSERT([self currentWindow] != nil);
318
319 #ifndef NP_NO_QUICKDRAW
320     // If drawing with QuickDraw, fix the window port so that it has the same bounds as the NSWindow's
321     // content view.  This makes it easier to convert between AppKit view and QuickDraw port coordinates.
322     if (drawingModel == NPDrawingModelQuickDraw)
323         [self fixWindowPort];
324 #endif
325
326     // Use AppKit to convert view coordinates to NSWindow coordinates.
327     NSRect boundsInWindow = [self convertRect:[self bounds] toView:nil];
328     NSRect visibleRectInWindow = [self convertRect:[self visibleRect] toView:nil];
329     
330     // Flip Y to convert NSWindow coordinates to top-left-based window coordinates.
331     float borderViewHeight = [[self currentWindow] frame].size.height;
332     boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow);
333     visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow);
334     
335 #ifndef NP_NO_QUICKDRAW
336     WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
337     ASSERT(windowRef);
338         
339     // Look at the Carbon port to convert top-left-based window coordinates into top-left-based content coordinates.
340     if (drawingModel == NPDrawingModelQuickDraw) {
341         ::Rect portBounds;
342         CGrafPtr port = GetWindowPort(windowRef);
343         GetPortBounds(port, &portBounds);
344
345         PixMap *pix = *GetPortPixMap(port);
346         boundsInWindow.origin.x += pix->bounds.left - portBounds.left;
347         boundsInWindow.origin.y += pix->bounds.top - portBounds.top;
348         visibleRectInWindow.origin.x += pix->bounds.left - portBounds.left;
349         visibleRectInWindow.origin.y += pix->bounds.top - portBounds.top;
350     }
351 #endif
352     
353     window.x = (int32)boundsInWindow.origin.x; 
354     window.y = (int32)boundsInWindow.origin.y;
355     window.width = static_cast<uint32>(NSWidth(boundsInWindow));
356     window.height = static_cast<uint32>(NSHeight(boundsInWindow));
357     
358     // "Clip-out" the plug-in when:
359     // 1) it's not really in a window or off-screen or has no height or width.
360     // 2) window.x is a "big negative number" which is how WebCore expresses off-screen widgets.
361     // 3) the window is miniaturized or the app is hidden
362     // 4) we're inside of viewWillMoveToWindow: with a nil window. In this case, superviews may already have nil 
363     // superviews and nil windows and results from convertRect:toView: are incorrect.
364     NSWindow *realWindow = [self window];
365     if (window.width <= 0 || window.height <= 0 || window.x < -100000
366             || realWindow == nil || [realWindow isMiniaturized]
367             || [NSApp isHidden]
368             || ![self superviewsHaveSuperviews]
369             || [self isHiddenOrHasHiddenAncestor]) {
370
371         // The following code tries to give plug-ins the same size they will eventually have.
372         // The specifiedWidth and specifiedHeight variables are used to predict the size that
373         // WebCore will eventually resize us to.
374
375         // The QuickTime plug-in has problems if you give it a width or height of 0.
376         // Since other plug-ins also might have the same sort of trouble, we make sure
377         // to always give plug-ins a size other than 0,0.
378
379         if (window.width <= 0)
380             window.width = specifiedWidth > 0 ? specifiedWidth : 100;
381         if (window.height <= 0)
382             window.height = specifiedHeight > 0 ? specifiedHeight : 100;
383
384         window.clipRect.bottom = window.clipRect.top;
385         window.clipRect.left = window.clipRect.right;
386     } else {
387         getNPRect(visibleRectInWindow, window.clipRect);
388     }
389     
390     // Save the port state, set up the port for entry into the plugin
391     PortState portState;
392     switch (drawingModel) {
393 #ifndef NP_NO_QUICKDRAW
394         case NPDrawingModelQuickDraw: {
395             // Set up NS_Port.
396             ::Rect portBounds;
397             CGrafPtr port = GetWindowPort(windowRef);
398             GetPortBounds(port, &portBounds);
399             nPort.qdPort.port = port;
400             nPort.qdPort.portx = (int32)-boundsInWindow.origin.x;
401             nPort.qdPort.porty = (int32)-boundsInWindow.origin.y;
402             window.window = &nPort;
403
404             PortState_QD *qdPortState = (PortState_QD*)malloc(sizeof(PortState_QD));
405             portState = (PortState)qdPortState;
406             
407             GetGWorld(&qdPortState->oldPort, &qdPortState->oldDevice);    
408
409             qdPortState->oldOrigin.h = portBounds.left;
410             qdPortState->oldOrigin.v = portBounds.top;
411
412             qdPortState->oldClipRegion = NewRgn();
413             GetPortClipRegion(port, qdPortState->oldClipRegion);
414             
415             qdPortState->oldVisibleRegion = NewRgn();
416             GetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
417             
418             RgnHandle clipRegion = NewRgn();
419             qdPortState->clipRegion = clipRegion;
420
421             CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
422             if (currentContext && WKCGContextIsBitmapContext(currentContext)) {
423                 // We use WKCGContextIsBitmapContext here, because if we just called CGBitmapContextGetData
424                 // on any context, we'd log to the console every time. But even if WKCGContextIsBitmapContext
425                 // returns true, it still might not be a context we need to create a GWorld for; for example
426                 // transparency layers will return true, but return 0 for CGBitmapContextGetData.
427                 void* offscreenData = CGBitmapContextGetData(currentContext);
428                 if (offscreenData) {
429                     // If the current context is an offscreen bitmap, then create a GWorld for it.
430                     ::Rect offscreenBounds;
431                     offscreenBounds.top = 0;
432                     offscreenBounds.left = 0;
433                     offscreenBounds.right = CGBitmapContextGetWidth(currentContext);
434                     offscreenBounds.bottom = CGBitmapContextGetHeight(currentContext);
435                     GWorldPtr newOffscreenGWorld;
436                     QDErr err = NewGWorldFromPtr(&newOffscreenGWorld,
437                         getQDPixelFormatForBitmapContext(currentContext), &offscreenBounds, 0, 0, 0,
438                         static_cast<char*>(offscreenData), CGBitmapContextGetBytesPerRow(currentContext));
439                     ASSERT(newOffscreenGWorld && !err);
440                     if (!err) {
441                         if (offscreenGWorld)
442                             DisposeGWorld(offscreenGWorld);
443                         offscreenGWorld = newOffscreenGWorld;
444
445                         SetGWorld(offscreenGWorld, NULL);
446
447                         port = offscreenGWorld;
448
449                         nPort.qdPort.port = port;
450                         boundsInWindow = [self bounds];
451                         
452                         // Generate a QD origin based on the current affine transform for currentContext.
453                         CGAffineTransform offscreenMatrix = CGContextGetCTM(currentContext);
454                         CGPoint origin = {0,0};
455                         CGPoint axisFlip = {1,1};
456                         origin = CGPointApplyAffineTransform(origin, offscreenMatrix);
457                         axisFlip = CGPointApplyAffineTransform(axisFlip, offscreenMatrix);
458                         
459                         // Quartz bitmaps have origins at the bottom left, but the axes may be inverted, so handle that.
460                         origin.x = offscreenBounds.left - origin.x * (axisFlip.x - origin.x);
461                         origin.y = offscreenBounds.bottom + origin.y * (axisFlip.y - origin.y);
462                         
463                         nPort.qdPort.portx = static_cast<int32>(-boundsInWindow.origin.x + origin.x);
464                         nPort.qdPort.porty = static_cast<int32>(-boundsInWindow.origin.y - origin.y);
465                         window.x = 0;
466                         window.y = 0;
467                         window.window = &nPort;
468
469                         // Use the clip bounds from the context instead of the bounds we created
470                         // from the window above.
471                         getNPRect(CGRectOffset(CGContextGetClipBoundingBox(currentContext), -origin.x, origin.y), window.clipRect);
472                     }
473                 }
474             }
475
476             MacSetRectRgn(clipRegion,
477                 window.clipRect.left + nPort.qdPort.portx, window.clipRect.top + nPort.qdPort.porty,
478                 window.clipRect.right + nPort.qdPort.portx, window.clipRect.bottom + nPort.qdPort.porty);
479             
480             // Clip to dirty region so plug-in does not draw over already-drawn regions of the window that are
481             // not going to be redrawn this update.  This forces plug-ins to play nice with z-index ordering.
482             if (forUpdate) {
483                 RgnHandle viewClipRegion = NewRgn();
484                 
485                 // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
486                 // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
487                 // knows about the true set of dirty rects.
488                 NSView *opaqueAncestor = [self opaqueAncestor];
489                 const NSRect *dirtyRects;
490                 NSInteger dirtyRectCount, dirtyRectIndex;
491                 [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount];
492
493                 for (dirtyRectIndex = 0; dirtyRectIndex < dirtyRectCount; dirtyRectIndex++) {
494                     NSRect dirtyRect = [self convertRect:dirtyRects[dirtyRectIndex] fromView:opaqueAncestor];
495                     if (!NSEqualSizes(dirtyRect.size, NSZeroSize)) {
496                         // Create a region for this dirty rect
497                         RgnHandle dirtyRectRegion = NewRgn();
498                         SetRectRgn(dirtyRectRegion, static_cast<short>(NSMinX(dirtyRect)), static_cast<short>(NSMinY(dirtyRect)), static_cast<short>(NSMaxX(dirtyRect)), static_cast<short>(NSMaxY(dirtyRect)));
499                         
500                         // Union this dirty rect with the rest of the dirty rects
501                         UnionRgn(viewClipRegion, dirtyRectRegion, viewClipRegion);
502                         DisposeRgn(dirtyRectRegion);
503                     }
504                 }
505             
506                 // Intersect the dirty region with the clip region, so that we only draw over dirty parts
507                 SectRgn(clipRegion, viewClipRegion, clipRegion);
508                 DisposeRgn(viewClipRegion);
509             }
510
511             // Switch to the port and set it up.
512             SetPort(port);
513             PenNormal();
514             ForeColor(blackColor);
515             BackColor(whiteColor);
516             SetOrigin(nPort.qdPort.portx, nPort.qdPort.porty);
517             SetPortClipRegion(nPort.qdPort.port, clipRegion);
518
519             if (forUpdate) {
520                 // AppKit may have tried to help us by doing a BeginUpdate.
521                 // But the invalid region at that level didn't include AppKit's notion of what was not valid.
522                 // We reset the port's visible region to counteract what BeginUpdate did.
523                 SetPortVisibleRegion(nPort.qdPort.port, clipRegion);
524                 InvalWindowRgn(windowRef, clipRegion);
525             }
526             
527             qdPortState->forUpdate = forUpdate;
528             break;
529         }
530 #endif /* NP_NO_QUICKDRAW */
531
532         case NPDrawingModelCoreGraphics: {            
533             ASSERT([NSView focusView] == self);
534
535             CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
536
537             PortState_CG *cgPortState = (PortState_CG *)malloc(sizeof(PortState_CG));
538             portState = (PortState)cgPortState;
539             cgPortState->context = context;
540             
541             // Update the plugin's window/context
542 #ifdef NP_NO_CARBON
543             nPort.cgPort.window = (NPNSWindow *)[self currentWindow];
544 #else
545             nPort.cgPort.window = eventHandler->platformWindow([self currentWindow]);
546 #endif /* NP_NO_CARBON */
547             nPort.cgPort.context = context;
548             window.window = &nPort.cgPort;
549
550             // Save current graphics context's state; will be restored by -restorePortState:
551             CGContextSaveGState(context);
552             
553             // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
554             // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
555             // knows about the true set of dirty rects.
556             NSView *opaqueAncestor = [self opaqueAncestor];
557             const NSRect *dirtyRects;
558             NSInteger count;
559             [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&count];
560             Vector<CGRect, 16> convertedDirtyRects;
561             convertedDirtyRects.resize(count);
562             for (int i = 0; i < count; ++i)
563                 reinterpret_cast<NSRect&>(convertedDirtyRects[i]) = [self convertRect:dirtyRects[i] fromView:opaqueAncestor];
564             CGContextClipToRects(context, convertedDirtyRects.data(), count);
565
566             break;
567         }
568
569         case NPDrawingModelOpenGL: {
570             ASSERT([NSView focusView] == self);
571
572             // Clear the "current" window and context -- they will be assigned below (if all goes well)
573             nPort.aglPort.window = NULL;
574             nPort.aglPort.context = NULL;
575             
576             // Create AGL context if needed
577             if (![self _createAGLContextIfNeeded]) {
578                 LOG_ERROR("Could not create AGL context");
579                 return NULL;
580             }
581             
582             // Update the plugin's window/context
583 #ifdef NP_NO_CARBON
584             nPort.aglPort.window = (NPNSWindow *)[self currentWindow];
585 #else
586             nPort.aglPort.window = eventHandler->platformWindow([self currentWindow]);
587 #endif // NP_NO_CARBON
588             nPort.aglPort.context = [self _cglContext];
589             window.window = &nPort.aglPort;
590             
591             // Save/set current AGL context
592             PortState_GL *glPortState = (PortState_GL *)malloc(sizeof(PortState_GL));
593             portState = (PortState)glPortState;
594             glPortState->oldContext = aglGetCurrentContext();
595             aglSetCurrentContext(aglContext);
596             
597             // Adjust viewport according to clip
598             switch (window.type) {
599                 case NPWindowTypeWindow:
600                     glViewport(static_cast<GLint>(NSMinX(boundsInWindow) - NSMinX(visibleRectInWindow)),
601                         static_cast<GLint>(NSMaxY(visibleRectInWindow) - NSMaxY(boundsInWindow)),
602                             window.width, window.height);
603                     break;
604                 
605                 case NPWindowTypeDrawable: {
606                     GLsizei width, height;
607                     if ([self _getAGLOffscreenBuffer:NULL width:&width height:&height])
608                         glViewport(0, 0, width, height);
609                     break;
610                 }
611                 
612                 default:
613                     ASSERT_NOT_REACHED();
614                     break;
615             }
616             break;
617         }
618         
619         default:
620             ASSERT_NOT_REACHED();
621             portState = NULL;
622             break;
623     }
624     
625     return portState;
626 }
627
628 - (PortState)saveAndSetNewPortState
629 {
630     return [self saveAndSetNewPortStateForUpdate:NO];
631 }
632
633 - (void)restorePortState:(PortState)portState
634 {
635     ASSERT([self currentWindow]);
636     ASSERT(portState);
637     
638     switch (drawingModel) {
639 #ifndef NP_NO_QUICKDRAW
640         case NPDrawingModelQuickDraw: {
641             PortState_QD *qdPortState = (PortState_QD *)portState;
642             WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
643             CGrafPtr port = GetWindowPort(windowRef);
644
645             SetPort(port);
646
647             if (qdPortState->forUpdate)
648                 ValidWindowRgn(windowRef, qdPortState->clipRegion);
649
650             SetOrigin(qdPortState->oldOrigin.h, qdPortState->oldOrigin.v);
651
652             SetPortClipRegion(port, qdPortState->oldClipRegion);
653             if (qdPortState->forUpdate)
654                 SetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
655
656             DisposeRgn(qdPortState->oldClipRegion);
657             DisposeRgn(qdPortState->oldVisibleRegion);
658             DisposeRgn(qdPortState->clipRegion);
659
660             SetGWorld(qdPortState->oldPort, qdPortState->oldDevice);
661             break;
662         }
663 #endif /* NP_NO_QUICKDRAW */
664         
665         case NPDrawingModelCoreGraphics:
666             ASSERT([NSView focusView] == self);
667             ASSERT(((PortState_CG *)portState)->context == nPort.cgPort.context);
668             CGContextRestoreGState(nPort.cgPort.context);
669             break;
670         
671         case NPDrawingModelOpenGL:
672             aglSetCurrentContext(((PortState_GL *)portState)->oldContext);
673             break;
674         
675         default:
676             ASSERT_NOT_REACHED();
677             break;
678     }
679 }
680
681 - (BOOL)sendEvent:(void*)event isDrawRect:(BOOL)eventIsDrawRect
682 {
683     if (![self window])
684         return NO;
685     ASSERT(event);
686        
687     if (!isStarted)
688         return NO;
689
690     ASSERT(NPP_HandleEvent);
691     
692     // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
693     // We probably don't want more general reentrancy protection; we are really
694     // protecting only against this one case, which actually comes up when
695     // you first install the SVG viewer plug-in.
696     if (inSetWindow)
697         return NO;
698
699     Frame* frame = core([self webFrame]);
700     if (!frame)
701         return NO;
702     Page* page = frame->page();
703     if (!page)
704         return NO;
705
706     bool wasDeferring = page->defersLoading();
707     if (!wasDeferring)
708         page->setDefersLoading(true);
709
710     // Can only send drawRect (updateEvt) to CoreGraphics and OpenGL plugins when actually drawing
711     ASSERT((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || !eventIsDrawRect || [NSView focusView] == self);
712     
713     PortState portState;
714     if ((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || eventIsDrawRect) {
715         // In CoreGraphics or OpenGL mode, the port state only needs to be saved/set when redrawing the plug-in view.  The plug-in is not
716         // allowed to draw at any other time.
717         portState = [self saveAndSetNewPortStateForUpdate:eventIsDrawRect];
718         
719         // We may have changed the window, so inform the plug-in.
720         [self setWindowIfNecessary];
721     } else
722         portState = NULL;
723     
724 #if !defined(NDEBUG) && !defined(NP_NO_QUICKDRAW)
725     // Draw green to help debug.
726     // If we see any green we know something's wrong.
727     // Note that PaintRect() only works for QuickDraw plugins; otherwise the current QD port is undefined.
728     if (drawingModel == NPDrawingModelQuickDraw && !isTransparent && eventIsDrawRect) {
729         ForeColor(greenColor);
730         const ::Rect bigRect = { -10000, -10000, 10000, 10000 };
731         PaintRect(&bigRect);
732         ForeColor(blackColor);
733     }
734 #endif
735     
736     // Temporarily retain self in case the plug-in view is released while sending an event. 
737     [[self retain] autorelease];
738     
739     BOOL acceptedEvent;
740     [self willCallPlugInFunction];
741     {
742         KJS::JSLock::DropAllLocks dropAllLocks;
743         acceptedEvent = NPP_HandleEvent(plugin, event);
744     }
745     [self didCallPlugInFunction];
746         
747     if (portState) {
748         if ([self currentWindow])
749             [self restorePortState:portState];
750         free(portState);
751     }
752
753     if (!wasDeferring)
754         page->setDefersLoading(false);
755             
756     return acceptedEvent;
757 }
758
759 - (void)sendActivateEvent:(BOOL)activate
760 {
761     eventHandler->windowFocusChanged(activate);
762 }
763
764 - (void)sendDrawRectEvent:(NSRect)rect
765 {
766     eventHandler->drawRect(rect);
767 }
768
769 - (void)stopTimers
770 {
771     if (eventHandler)
772         eventHandler->stopTimers();
773     
774     shouldFireTimers = NO;
775     
776     if (!timers)
777         return;
778
779     HashMap<uint32, PluginTimer*>::const_iterator end = timers->end();
780     for (HashMap<uint32, PluginTimer*>::const_iterator it = timers->begin(); it != end; ++it) {
781         PluginTimer* timer = it->second;
782         timer->stop();
783     }    
784 }
785
786 - (void)restartTimers
787 {
788     ASSERT([self window]);
789     
790     if (shouldFireTimers)
791         [self stopTimers];
792     
793     if (!isStarted || [[self window] isMiniaturized])
794         return;
795
796     shouldFireTimers = YES;
797     
798     // If the plugin is completely obscured (scrolled out of view, for example), then we will
799     // send null events at a reduced rate.
800     eventHandler->startTimers(isCompletelyObscured);
801     
802     if (!timers)
803         return;
804     
805     HashMap<uint32, PluginTimer*>::const_iterator end = timers->end();
806     for (HashMap<uint32, PluginTimer*>::const_iterator it = timers->begin(); it != end; ++it) {
807         PluginTimer* timer = it->second;
808         ASSERT(!timer->isActive());
809         timer->start(isCompletelyObscured);
810     }    
811 }
812
813 - (BOOL)acceptsFirstResponder
814 {
815     return YES;
816 }
817
818 - (void)setHasFocus:(BOOL)flag
819 {
820     if (hasFocus == flag)
821         return;
822     
823     hasFocus = flag;
824     
825     // We need to null check the event handler here because
826     // the plug-in view can resign focus after it's been stopped
827     // and the event handler has been deleted.
828     if (eventHandler)
829         eventHandler->focusChanged(hasFocus);    
830 }
831
832 - (BOOL)becomeFirstResponder
833 {
834     [self setHasFocus:YES];
835     return YES;
836 }
837
838 - (BOOL)resignFirstResponder
839 {
840     [self setHasFocus:NO];    
841     return YES;
842 }
843
844 // AppKit doesn't call mouseDown or mouseUp on right-click. Simulate control-click
845 // mouseDown and mouseUp so plug-ins get the right-click event as they do in Carbon (3125743).
846 - (void)rightMouseDown:(NSEvent *)theEvent
847 {
848     [self mouseDown:theEvent];
849 }
850
851 - (void)rightMouseUp:(NSEvent *)theEvent
852 {
853     [self mouseUp:theEvent];
854 }
855
856 - (void)mouseDown:(NSEvent *)theEvent
857 {
858     eventHandler->mouseDown(theEvent);
859 }
860
861 - (void)mouseUp:(NSEvent *)theEvent
862 {
863     eventHandler->mouseUp(theEvent);
864 }
865
866 - (void)mouseEntered:(NSEvent *)theEvent
867 {
868     eventHandler->mouseEntered(theEvent);
869 }
870
871 - (void)mouseExited:(NSEvent *)theEvent
872 {
873     eventHandler->mouseExited(theEvent);
874     
875     // 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
876     // current cursor is otherwise.  Therefore we have no choice but to unconditionally reset the cursor when the mouse exits the plugin.
877     [[NSCursor arrowCursor] set];
878 }
879
880 // We can't name this method mouseMoved because we don't want to override 
881 // the NSView mouseMoved implementation.
882 - (void)handleMouseMoved:(NSEvent *)theEvent
883 {
884     eventHandler->mouseMoved(theEvent);
885 }
886     
887 - (void)mouseDragged:(NSEvent *)theEvent
888 {
889     eventHandler->mouseDragged(theEvent);
890 }
891
892 - (void)scrollWheel:(NSEvent *)theEvent
893 {
894     if (!eventHandler->scrollWheel(theEvent))
895         [super scrollWheel:theEvent];
896 }
897
898 - (void)keyUp:(NSEvent *)theEvent
899 {
900     eventHandler->keyUp(theEvent);
901 }
902
903 - (void)keyDown:(NSEvent *)theEvent
904 {
905     eventHandler->keyDown(theEvent);
906 }
907
908 - (void)flagsChanged:(NSEvent *)theEvent
909 {
910     eventHandler->flagsChanged(theEvent);
911 }
912
913 - (void)cut:(id)sender
914 {
915     eventHandler->keyDown([NSApp currentEvent]);
916 }
917
918 - (void)copy:(id)sender
919 {
920     eventHandler->keyDown([NSApp currentEvent]);
921 }
922
923 - (void)paste:(id)sender
924 {
925     eventHandler->keyDown([NSApp currentEvent]);
926 }
927
928 - (void)selectAll:(id)sender
929 {
930     eventHandler->keyDown([NSApp currentEvent]);
931 }
932
933 #pragma mark WEB_NETSCAPE_PLUGIN
934
935 - (BOOL)isNewWindowEqualToOldWindow
936 {
937     if (window.x != lastSetWindow.x)
938         return NO;
939     if (window.y != lastSetWindow.y)
940         return NO;
941     if (window.width != lastSetWindow.width)
942         return NO;
943     if (window.height != lastSetWindow.height)
944         return NO;
945     if (window.clipRect.top != lastSetWindow.clipRect.top)
946         return NO;
947     if (window.clipRect.left != lastSetWindow.clipRect.left)
948         return NO;
949     if (window.clipRect.bottom  != lastSetWindow.clipRect.bottom)
950         return NO;
951     if (window.clipRect.right != lastSetWindow.clipRect.right)
952         return NO;
953     if (window.type != lastSetWindow.type)
954         return NO;
955     
956     switch (drawingModel) {
957 #ifndef NP_NO_QUICKDRAW
958         case NPDrawingModelQuickDraw:
959             if (nPort.qdPort.portx != lastSetPort.qdPort.portx)
960                 return NO;
961             if (nPort.qdPort.porty != lastSetPort.qdPort.porty)
962                 return NO;
963             if (nPort.qdPort.port != lastSetPort.qdPort.port)
964                 return NO;
965         break;
966 #endif /* NP_NO_QUICKDRAW */
967             
968         case NPDrawingModelCoreGraphics:
969             if (nPort.cgPort.window != lastSetPort.cgPort.window)
970                 return NO;
971             if (nPort.cgPort.context != lastSetPort.cgPort.context)
972                 return NO;
973         break;
974             
975         case NPDrawingModelOpenGL:
976             if (nPort.aglPort.window != lastSetPort.aglPort.window)
977                 return NO;
978             if (nPort.aglPort.context != lastSetPort.aglPort.context)
979                 return NO;
980         break;
981         
982         default:
983             ASSERT_NOT_REACHED();
984         break;
985     }
986     
987     return YES;
988 }
989
990 - (void)updateAndSetWindow
991 {
992     // A plug-in can only update if it's (1) already been started (2) isn't stopped
993     // and (3) is able to draw on-screen. To meet condition (3) the plug-in must not
994     // be hidden and be attached to a window. QuickDraw plug-ins are an important
995     // excpetion to rule (3) because they manually must be told when to stop writing
996     // bits to the window backing store, thus to do so requires a new call to
997     // NPP_SetWindow() with an empty NPWindow struct.
998     if (!isStarted)
999         return;
1000 #ifdef NP_NO_QUICKDRAW
1001     if (![self canDraw])
1002         return;
1003 #else
1004     if (drawingModel != NPDrawingModelQuickDraw && ![self canDraw])
1005         return;
1006 #endif // NP_NO_QUICKDRAW
1007     
1008     BOOL didLockFocus = [NSView focusView] != self && [self lockFocusIfCanDraw];
1009     PortState portState = [self saveAndSetNewPortState];
1010     if (portState) {
1011         [self setWindowIfNecessary];
1012         [self restorePortState:portState];
1013         free(portState);
1014     }   
1015     if (didLockFocus)
1016         [self unlockFocus];
1017 }
1018
1019 - (void)setWindowIfNecessary
1020 {
1021     if (!isStarted) {
1022         return;
1023     }
1024     
1025     if (![self isNewWindowEqualToOldWindow]) {        
1026         // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
1027         // We probably don't want more general reentrancy protection; we are really
1028         // protecting only against this one case, which actually comes up when
1029         // you first install the SVG viewer plug-in.
1030         NPError npErr;
1031         ASSERT(!inSetWindow);
1032         
1033         inSetWindow = YES;
1034         
1035         // A CoreGraphics or OpenGL plugin's window may only be set while the plugin is being updated
1036         ASSERT((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || [NSView focusView] == self);
1037         
1038         [self willCallPlugInFunction];
1039         {
1040             KJS::JSLock::DropAllLocks dropAllLocks;
1041             npErr = NPP_SetWindow(plugin, &window);
1042         }
1043         [self didCallPlugInFunction];
1044         inSetWindow = NO;
1045
1046 #ifndef NDEBUG
1047         switch (drawingModel) {
1048 #ifndef NP_NO_QUICKDRAW
1049             case NPDrawingModelQuickDraw:
1050                 LOG(Plugins, "NPP_SetWindow (QuickDraw): %d, port=0x%08x, window.x:%d window.y:%d window.width:%d window.height:%d",
1051                 npErr, (int)nPort.qdPort.port, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1052             break;
1053 #endif /* NP_NO_QUICKDRAW */
1054             
1055             case NPDrawingModelCoreGraphics:
1056                 LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d",
1057                 npErr, nPort.cgPort.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1058             break;
1059
1060             case NPDrawingModelOpenGL:
1061                 LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d",
1062                 npErr, nPort.aglPort.window, nPort.aglPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1063             break;
1064             
1065             default:
1066                 ASSERT_NOT_REACHED();
1067             break;
1068         }
1069 #endif /* !defined(NDEBUG) */
1070         
1071         lastSetWindow = window;
1072         lastSetPort = nPort;
1073     }
1074 }
1075
1076 - (void)removeTrackingRect
1077 {
1078     if (trackingTag) {
1079         [self removeTrackingRect:trackingTag];
1080         trackingTag = 0;
1081
1082         // Do the following after setting trackingTag to 0 so we don't re-enter.
1083
1084         // Balance the retain in resetTrackingRect. Use autorelease in case we hold 
1085         // the last reference to the window during tear-down, to avoid crashing AppKit. 
1086         [[self window] autorelease];
1087     }
1088 }
1089
1090 - (void)resetTrackingRect
1091 {
1092     [self removeTrackingRect];
1093     if (isStarted) {
1094         // Retain the window so that removeTrackingRect can work after the window is closed.
1095         [[self window] retain];
1096         trackingTag = [self addTrackingRect:[self bounds] owner:self userData:nil assumeInside:NO];
1097     }
1098 }
1099
1100 + (void)setCurrentPluginView:(WebBaseNetscapePluginView *)view
1101 {
1102     currentPluginView = view;
1103 }
1104
1105 + (WebBaseNetscapePluginView *)currentPluginView
1106 {
1107     return currentPluginView;
1108 }
1109
1110 - (BOOL)canStart
1111 {
1112     return YES;
1113 }
1114
1115 - (void)didStart
1116 {
1117     if (_loadManually) {
1118         [self _redeliverStream];
1119         return;
1120     }
1121     
1122     // If the OBJECT/EMBED tag has no SRC, the URL is passed to us as "".
1123     // Check for this and don't start a load in this case.
1124     if (sourceURL != nil && ![sourceURL _web_isEmpty]) {
1125         NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:sourceURL];
1126         [request _web_setHTTPReferrer:core([self webFrame])->loader()->outgoingReferrer()];
1127         [self loadRequest:request inTarget:nil withNotifyData:nil sendNotification:NO];
1128     } 
1129 }
1130
1131 - (void)addWindowObservers
1132 {
1133     ASSERT([self window]);
1134
1135     NSWindow *theWindow = [self window];
1136     
1137     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
1138     [notificationCenter addObserver:self selector:@selector(windowWillClose:) 
1139                                name:NSWindowWillCloseNotification object:theWindow]; 
1140     [notificationCenter addObserver:self selector:@selector(windowBecameKey:)
1141                                name:NSWindowDidBecomeKeyNotification object:theWindow];
1142     [notificationCenter addObserver:self selector:@selector(windowResignedKey:)
1143                                name:NSWindowDidResignKeyNotification object:theWindow];
1144     [notificationCenter addObserver:self selector:@selector(windowDidMiniaturize:)
1145                                name:NSWindowDidMiniaturizeNotification object:theWindow];
1146     [notificationCenter addObserver:self selector:@selector(windowDidDeminiaturize:)
1147                                name:NSWindowDidDeminiaturizeNotification object:theWindow];
1148     
1149     [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchFromUser:)
1150                                name:LoginWindowDidSwitchFromUserNotification object:nil];
1151     [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchToUser:)
1152                                name:LoginWindowDidSwitchToUserNotification object:nil];
1153 }
1154
1155 - (void)removeWindowObservers
1156 {
1157     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
1158     [notificationCenter removeObserver:self name:NSWindowWillCloseNotification        object:nil]; 
1159     [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification     object:nil];
1160     [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification     object:nil];
1161     [notificationCenter removeObserver:self name:NSWindowDidMiniaturizeNotification   object:nil];
1162     [notificationCenter removeObserver:self name:NSWindowDidDeminiaturizeNotification object:nil];
1163     [notificationCenter removeObserver:self name:LoginWindowDidSwitchFromUserNotification   object:nil];
1164     [notificationCenter removeObserver:self name:LoginWindowDidSwitchToUserNotification     object:nil];
1165 }
1166
1167 - (BOOL)start
1168 {
1169     ASSERT([self currentWindow]);
1170     
1171     if (isStarted)
1172         return YES;
1173
1174     if (![self canStart])
1175         return NO;
1176     
1177     ASSERT([self webView]);
1178     
1179     if (![[[self webView] preferences] arePlugInsEnabled])
1180         return NO;
1181
1182     // Open the plug-in package so it remains loaded while our plugin uses it
1183     [pluginPackage open];
1184     
1185     // Initialize drawingModel to an invalid value so that we can detect when the plugin does not specify a drawingModel
1186     drawingModel = (NPDrawingModel)-1;
1187     
1188     // Initialize eventModel to an invalid value so that we can detect when the plugin does not specify an event model.
1189     eventModel = (NPEventModel)-1;
1190     
1191     // Plug-ins are "windowed" by default.  On MacOS, windowed plug-ins share the same window and graphics port as the main
1192     // browser window.  Windowless plug-ins are rendered off-screen, then copied into the main browser window.
1193     window.type = NPWindowTypeWindow;
1194     
1195     NPError npErr = [self _createPlugin];
1196     if (npErr != NPERR_NO_ERROR) {
1197         LOG_ERROR("NPP_New failed with error: %d", npErr);
1198         [self _destroyPlugin];
1199         [pluginPackage close];
1200         return NO;
1201     }
1202     
1203     if (drawingModel == (NPDrawingModel)-1) {
1204 #ifndef NP_NO_QUICKDRAW
1205         // Default to QuickDraw if the plugin did not specify a drawing model.
1206         drawingModel = NPDrawingModelQuickDraw;
1207 #else
1208         // QuickDraw is not available, so we can't default to it. Instead, default to CoreGraphics.
1209         drawingModel = NPDrawingModelCoreGraphics;
1210 #endif
1211     }
1212
1213     if (eventModel == (NPEventModel)-1) {
1214         // If the plug-in did not specify a drawing model we default to Carbon when it is available.
1215 #ifndef NP_NO_CARBON
1216         eventModel = NPEventModelCarbon;
1217 #else
1218         eventModel = NPEventModelCocoa;
1219 #endif // NP_NO_CARBON
1220     }
1221
1222 #ifndef NP_NO_CARBON
1223     if (eventModel == NPEventModelCocoa &&
1224         drawingModel == NPDrawingModelQuickDraw) {
1225         LOG(Plugins, "Plugin can't use use Cocoa event model with QuickDraw drawing model: %@", pluginPackage);
1226         [self _destroyPlugin];
1227         [pluginPackage close];
1228         
1229         return NO;
1230     }        
1231 #endif // NP_NO_CARBON
1232     
1233     // Create the event handler
1234     eventHandler = WebNetscapePluginEventHandler::create(self);
1235     
1236     isStarted = YES;
1237         
1238     [self updateAndSetWindow];
1239
1240     if ([self window]) {
1241         [self addWindowObservers];
1242         if ([[self window] isKeyWindow]) {
1243             [self sendActivateEvent:YES];
1244         }
1245         [self restartTimers];
1246     }
1247
1248     [self resetTrackingRect];
1249     
1250     [self didStart];
1251     
1252     return YES;
1253 }
1254
1255 - (void)stop
1256 {
1257     // If we're already calling a plug-in function, do not call NPP_Destroy().  The plug-in function we are calling
1258     // may assume that its instance->pdata, or other memory freed by NPP_Destroy(), is valid and unchanged until said
1259     // plugin-function returns.
1260     // See <rdar://problem/4480737>.
1261     if (pluginFunctionCallDepth > 0) {
1262         shouldStopSoon = YES;
1263         return;
1264     }
1265     
1266     [self removeTrackingRect];
1267
1268     if (!isStarted)
1269         return;
1270     
1271     isStarted = NO;
1272     // To stop active streams it's necessary to invoke makeObjectsPerformSelector on a copy 
1273     // of streams. This is because calling -[WebNetscapePluginStream stop] also has the side effect
1274     // of removing a stream from this collection.
1275     NSArray *streamsCopy = [streams copy];
1276     [streamsCopy makeObjectsPerformSelector:@selector(stop)];
1277     [streamsCopy release];
1278    
1279     // Stop the timers
1280     [self stopTimers];
1281     
1282     // Stop notifications and callbacks.
1283     [self removeWindowObservers];
1284     [[pendingFrameLoads allKeys] makeObjectsPerformSelector:@selector(_setInternalLoadDelegate:) withObject:nil];
1285     [NSObject cancelPreviousPerformRequestsWithTarget:self];
1286
1287     // Setting the window type to 0 ensures that NPP_SetWindow will be called if the plug-in is restarted.
1288     lastSetWindow.type = (NPWindowType)0;
1289     
1290     [self _destroyPlugin];
1291     [pluginPackage close];
1292     
1293     delete eventHandler;
1294     eventHandler = 0;
1295     
1296     if (drawingModel == NPDrawingModelOpenGL)
1297         [self _destroyAGLContext];
1298 }
1299
1300 - (BOOL)isStarted
1301 {
1302     return isStarted;
1303 }
1304
1305 - (NPEventModel)eventModel
1306 {
1307     return eventModel;
1308 }
1309
1310 - (WebDataSource *)dataSource
1311 {
1312     WebFrame *webFrame = kit(core(element)->document()->frame());
1313     return [webFrame _dataSource];
1314 }
1315
1316 - (WebFrame *)webFrame
1317 {
1318     return [[self dataSource] webFrame];
1319 }
1320
1321 - (WebView *)webView
1322 {
1323     return [[self webFrame] webView];
1324 }
1325
1326 - (NSWindow *)currentWindow
1327 {
1328     return [self window] ? [self window] : [[self webView] hostWindow];
1329 }
1330
1331 - (NPP)plugin
1332 {
1333     return plugin;
1334 }
1335
1336 - (WebNetscapePluginPackage *)pluginPackage
1337 {
1338     return pluginPackage;
1339 }
1340
1341 - (void)setPluginPackage:(WebNetscapePluginPackage *)thePluginPackage;
1342 {
1343     [thePluginPackage retain];
1344     [pluginPackage release];
1345     pluginPackage = thePluginPackage;
1346
1347     NPP_New =           [pluginPackage NPP_New];
1348     NPP_Destroy =       [pluginPackage NPP_Destroy];
1349     NPP_SetWindow =     [pluginPackage NPP_SetWindow];
1350     NPP_NewStream =     [pluginPackage NPP_NewStream];
1351     NPP_WriteReady =    [pluginPackage NPP_WriteReady];
1352     NPP_Write =         [pluginPackage NPP_Write];
1353     NPP_StreamAsFile =  [pluginPackage NPP_StreamAsFile];
1354     NPP_DestroyStream = [pluginPackage NPP_DestroyStream];
1355     NPP_HandleEvent =   [pluginPackage NPP_HandleEvent];
1356     NPP_URLNotify =     [pluginPackage NPP_URLNotify];
1357     NPP_GetValue =      [pluginPackage NPP_GetValue];
1358     NPP_SetValue =      [pluginPackage NPP_SetValue];
1359     NPP_Print =         [pluginPackage NPP_Print];
1360 }
1361
1362 - (void)setMIMEType:(NSString *)theMIMEType
1363 {
1364     NSString *type = [theMIMEType copy];
1365     [MIMEType release];
1366     MIMEType = type;
1367 }
1368
1369 - (void)setBaseURL:(NSURL *)theBaseURL
1370 {
1371     [theBaseURL retain];
1372     [baseURL release];
1373     baseURL = theBaseURL;
1374 }
1375
1376 - (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values;
1377 {
1378     ASSERT([keys count] == [values count]);
1379     
1380     // Convert the attributes to 2 C string arrays.
1381     // These arrays are passed to NPP_New, but the strings need to be
1382     // modifiable and live the entire life of the plugin.
1383
1384     // The Java plug-in requires the first argument to be the base URL
1385     if ([MIMEType isEqualToString:@"application/x-java-applet"]) {
1386         cAttributes = (char **)malloc(([keys count] + 1) * sizeof(char *));
1387         cValues = (char **)malloc(([values count] + 1) * sizeof(char *));
1388         cAttributes[0] = strdup("DOCBASE");
1389         cValues[0] = strdup([baseURL _web_URLCString]);
1390         argsCount++;
1391     } else {
1392         cAttributes = (char **)malloc([keys count] * sizeof(char *));
1393         cValues = (char **)malloc([values count] * sizeof(char *));
1394     }
1395
1396     BOOL isWMP = [[[pluginPackage bundle] bundleIdentifier] isEqualToString:@"com.microsoft.WMP.defaultplugin"];
1397     
1398     unsigned i;
1399     unsigned count = [keys count];
1400     for (i = 0; i < count; i++) {
1401         NSString *key = [keys objectAtIndex:i];
1402         NSString *value = [values objectAtIndex:i];
1403         if ([key _webkit_isCaseInsensitiveEqualToString:@"height"]) {
1404             specifiedHeight = [value intValue];
1405         } else if ([key _webkit_isCaseInsensitiveEqualToString:@"width"]) {
1406             specifiedWidth = [value intValue];
1407         }
1408         // Avoid Window Media Player crash when these attributes are present.
1409         if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) {
1410             continue;
1411         }
1412         cAttributes[argsCount] = strdup([key UTF8String]);
1413         cValues[argsCount] = strdup([value UTF8String]);
1414         LOG(Plugins, "%@ = %@", key, value);
1415         argsCount++;
1416     }
1417 }
1418
1419 - (void)setMode:(int)theMode
1420 {
1421     mode = theMode;
1422 }
1423
1424 #pragma mark NSVIEW
1425
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
1435 {
1436     [super initWithFrame:frame];
1437  
1438     streams = [[NSMutableArray alloc] init];
1439     pendingFrameLoads = [[NSMutableDictionary alloc] init];    
1440     
1441     // load the plug-in if it is not already loaded
1442     if (![thePluginPackage load]) {
1443         [self release];
1444         return nil;
1445     }
1446     [self setPluginPackage:thePluginPackage];
1447     
1448     element = [anElement retain];
1449     sourceURL = [theURL retain];
1450     
1451     [self setMIMEType:MIME];
1452     [self setBaseURL:theBaseURL];
1453     [self setAttributeKeys:keys andValues:values];
1454     if (loadManually)
1455         [self setMode:NP_FULL];
1456     else
1457         [self setMode:NP_EMBED];
1458     
1459     _loadManually = loadManually;
1460     
1461     return self;
1462 }
1463
1464 - (id)initWithFrame:(NSRect)frame
1465 {
1466     ASSERT_NOT_REACHED();
1467     return nil;
1468 }
1469
1470 - (void)fini
1471 {
1472 #ifndef NP_NO_QUICKDRAW
1473     if (offscreenGWorld)
1474         DisposeGWorld(offscreenGWorld);
1475 #endif
1476
1477     unsigned i;
1478     for (i = 0; i < argsCount; i++) {
1479         free(cAttributes[i]);
1480         free(cValues[i]);
1481     }
1482     free(cAttributes);
1483     free(cValues);
1484     
1485     ASSERT(!eventHandler);
1486     
1487     if (timers) {
1488         deleteAllValues(*timers);
1489         delete timers;
1490     }    
1491 }
1492
1493 - (void)disconnectStream:(WebBaseNetscapePluginStream*)stream
1494 {
1495     [streams removeObjectIdenticalTo:stream];    
1496 }
1497
1498 - (void)dealloc
1499 {
1500     ASSERT(!isStarted);
1501
1502     [sourceURL release];
1503     [_manualStream release];
1504     [_error release];
1505     
1506     [pluginPackage release];
1507     [streams release];
1508     [MIMEType release];
1509     [baseURL release];
1510     [pendingFrameLoads release];
1511     [element release];
1512     
1513     ASSERT(!plugin);
1514     ASSERT(!aglWindow);
1515     ASSERT(!aglContext);
1516
1517     [self fini];
1518
1519     [super dealloc];
1520 }
1521
1522 - (void)finalize
1523 {
1524     ASSERT_MAIN_THREAD();
1525     ASSERT(!isStarted);
1526
1527     [self fini];
1528
1529     [super finalize];
1530 }
1531
1532 - (void)drawRect:(NSRect)rect
1533 {
1534     if (!isStarted) {
1535         return;
1536     }
1537     
1538     if ([NSGraphicsContext currentContextDrawingToScreen])
1539         [self sendDrawRectEvent:rect];
1540     else {
1541         NSBitmapImageRep *printedPluginBitmap = [self _printedPluginBitmap];
1542         if (printedPluginBitmap) {
1543             // Flip the bitmap before drawing because the QuickDraw port is flipped relative
1544             // to this view.
1545             CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
1546             CGContextSaveGState(cgContext);
1547             NSRect bounds = [self bounds];
1548             CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
1549             CGContextScaleCTM(cgContext, 1.0f, -1.0f);
1550             [printedPluginBitmap drawInRect:bounds];
1551             CGContextRestoreGState(cgContext);
1552         }
1553     }
1554     
1555     // If this is a windowless OpenGL plugin, blit its contents back into this view.  The plug-in just drew into the offscreen context.
1556     if (drawingModel == NPDrawingModelOpenGL && window.type == NPWindowTypeDrawable) {
1557         NSImage *aglOffscreenImage = [self _aglOffscreenImageForDrawingInRect:rect];
1558         if (aglOffscreenImage) {
1559             // Flip the context before drawing because the CGL context is flipped relative to this view.
1560             CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
1561             CGContextSaveGState(cgContext);
1562             NSRect bounds = [self bounds];
1563             CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
1564             CGContextScaleCTM(cgContext, 1.0f, -1.0f);
1565             
1566             // Copy 'rect' from the offscreen buffer to this view (the flip above makes this sort of tricky)
1567             NSRect flippedRect = rect;
1568             flippedRect.origin.y = NSMaxY(bounds) - NSMaxY(flippedRect);
1569             [aglOffscreenImage drawInRect:flippedRect fromRect:flippedRect operation:NSCompositeSourceOver fraction:1.0f];
1570             CGContextRestoreGState(cgContext);
1571         }
1572     }
1573 }
1574
1575 - (BOOL)isFlipped
1576 {
1577     return YES;
1578 }
1579
1580 - (void)renewGState
1581 {
1582     [super renewGState];
1583     
1584     // -renewGState is called whenever the view's geometry changes.  It's a little hacky to override this method, but
1585     // much safer than walking up the view hierarchy and observing frame/bounds changed notifications, since you don't
1586     // have to track subsequent changes to the view hierarchy and add/remove notification observers.
1587     // NSOpenGLView uses the exact same technique to reshape its OpenGL surface.
1588     [self _viewHasMoved];
1589 }
1590
1591 #ifndef NP_NO_QUICKDRAW
1592 -(void)tellQuickTimeToChill
1593 {
1594     ASSERT(drawingModel == NPDrawingModelQuickDraw);
1595     
1596     // Make a call to the secret QuickDraw API that makes QuickTime calm down.
1597     WindowRef windowRef = (WindowRef)[[self window] windowRef];
1598     if (!windowRef) {
1599         return;
1600     }
1601     CGrafPtr port = GetWindowPort(windowRef);
1602     ::Rect bounds;
1603     GetPortBounds(port, &bounds);
1604     WKCallDrawingNotification(port, &bounds);
1605 }
1606 #endif /* NP_NO_QUICKDRAW */
1607
1608 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
1609 {
1610 #ifndef NP_NO_QUICKDRAW
1611     if (drawingModel == NPDrawingModelQuickDraw)
1612         [self tellQuickTimeToChill];
1613 #endif
1614
1615     // We must remove the tracking rect before we move to the new window.
1616     // Once we move to the new window, it will be too late.
1617     [self removeTrackingRect];
1618     [self removeWindowObservers];
1619     
1620     // Workaround for: <rdar://problem/3822871> resignFirstResponder is not sent to first responder view when it is removed from the window
1621     [self setHasFocus:NO];
1622
1623     if (!newWindow) {
1624         // Hide the AGL child window
1625         if (drawingModel == NPDrawingModelOpenGL)
1626             [self _hideAGLWindow];
1627         
1628         if ([[self webView] hostWindow]) {
1629             // View will be moved out of the actual window but it still has a host window.
1630             [self stopTimers];
1631         } else {
1632             // View will have no associated windows.
1633             [self stop];
1634
1635             // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
1636             // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
1637             [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1638         }
1639     }
1640 }
1641
1642 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
1643 {
1644     if (!newSuperview) {
1645         // Stop the plug-in when it is removed from its superview.  It is not sufficient to do this in -viewWillMoveToWindow:nil, because
1646         // the WebView might still has a hostWindow at that point, which prevents the plug-in from being destroyed.
1647         // There is no need to start the plug-in when moving into a superview.  -viewDidMoveToWindow takes care of that.
1648         [self stop];
1649         
1650         // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
1651         // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
1652         [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1653     }
1654 }
1655
1656 - (void)viewDidMoveToWindow
1657 {
1658     [self resetTrackingRect];
1659     
1660     if ([self window]) {
1661         // While in the view hierarchy, observe WebPreferencesChangedNotification so that we can start/stop depending
1662         // on whether plugins are enabled.
1663         [[NSNotificationCenter defaultCenter] addObserver:self
1664                                               selector:@selector(preferencesHaveChanged:)
1665                                               name:WebPreferencesChangedNotification
1666                                               object:nil];
1667
1668         // View moved to an actual window. Start it if not already started.
1669         [self start];
1670         [self restartTimers];
1671         [self addWindowObservers];
1672     } else if ([[self webView] hostWindow]) {
1673         // View moved out of an actual window, but still has a host window.
1674         // Call setWindow to explicitly "clip out" the plug-in from sight.
1675         // FIXME: It would be nice to do this where we call stopNullEvents in viewWillMoveToWindow.
1676         [self updateAndSetWindow];
1677     }
1678 }
1679
1680 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
1681 {
1682     if (!hostWindow && ![self window]) {
1683         // View will have no associated windows.
1684         [self stop];
1685
1686         // Remove WebPreferencesChangedNotification observer -- we will observe once again when we move back into the window
1687         [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1688     }
1689 }
1690
1691 - (void)viewDidMoveToHostWindow
1692 {
1693     if ([[self webView] hostWindow]) {
1694         // View now has an associated window. Start it if not already started.
1695         [self start];
1696     }
1697 }
1698
1699 #pragma mark NOTIFICATIONS
1700
1701 - (void)windowWillClose:(NSNotification *)notification 
1702 {
1703     [self stop]; 
1704
1705
1706 - (void)windowBecameKey:(NSNotification *)notification
1707 {
1708     [self sendActivateEvent:YES];
1709     [self setNeedsDisplay:YES];
1710     [self restartTimers];
1711 #ifndef NP_NO_CARBON
1712     SetUserFocusWindow((WindowRef)[[self window] windowRef]);
1713 #endif // NP_NO_CARBON
1714 }
1715
1716 - (void)windowResignedKey:(NSNotification *)notification
1717 {
1718     [self sendActivateEvent:NO];
1719     [self setNeedsDisplay:YES];
1720     [self restartTimers];
1721 }
1722
1723 - (void)windowDidMiniaturize:(NSNotification *)notification
1724 {
1725     [self stopTimers];
1726 }
1727
1728 - (void)windowDidDeminiaturize:(NSNotification *)notification
1729 {
1730     [self stopTimers];
1731 }
1732
1733 - (void)loginWindowDidSwitchFromUser:(NSNotification *)notification
1734 {
1735     [self stopTimers];
1736 }
1737
1738 -(void)loginWindowDidSwitchToUser:(NSNotification *)notification
1739 {
1740     [self restartTimers];
1741 }
1742
1743 - (void)preferencesHaveChanged:(NSNotification *)notification
1744 {
1745     WebPreferences *preferences = [[self webView] preferences];
1746     BOOL arePlugInsEnabled = [preferences arePlugInsEnabled];
1747     
1748     if ([notification object] == preferences && isStarted != arePlugInsEnabled) {
1749         if (arePlugInsEnabled) {
1750             if ([self currentWindow]) {
1751                 [self start];
1752             }
1753         } else {
1754             [self stop];
1755             [self setNeedsDisplay:YES];
1756         }
1757     }
1758 }
1759
1760 - (NPObject *)createPluginScriptableObject
1761 {
1762     if (!NPP_GetValue || ![self isStarted])
1763         return NULL;
1764         
1765     NPObject *value = NULL;
1766     NPError error;
1767     [self willCallPlugInFunction];
1768     {
1769         KJS::JSLock::DropAllLocks dropAllLocks;
1770         error = NPP_GetValue(plugin, NPPVpluginScriptableNPObject, &value);
1771     }
1772     [self didCallPlugInFunction];
1773     if (error != NPERR_NO_ERROR)
1774         return NULL;
1775     
1776     return value;
1777 }
1778
1779 - (void)willCallPlugInFunction
1780 {
1781     ASSERT(plugin);
1782
1783     // Could try to prevent infinite recursion here, but it's probably not worth the effort.
1784     pluginFunctionCallDepth++;
1785 }
1786
1787 - (void)didCallPlugInFunction
1788 {
1789     ASSERT(pluginFunctionCallDepth > 0);
1790     pluginFunctionCallDepth--;
1791     
1792     // If -stop was called while we were calling into a plug-in function, and we're no longer
1793     // inside a plug-in function, stop now.
1794     if (pluginFunctionCallDepth == 0 && shouldStopSoon) {
1795         shouldStopSoon = NO;
1796         [self stop];
1797     }
1798 }
1799
1800 -(void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
1801 {
1802     ASSERT(_loadManually);
1803     ASSERT(!_manualStream);
1804     
1805     _manualStream = [[WebNetscapePluginStream alloc] initWithFrameLoader:core([self webFrame])->loader()];
1806 }
1807
1808 - (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
1809 {
1810     ASSERT(_loadManually);
1811     ASSERT(_manualStream);
1812     
1813     _dataLengthReceived += [data length];
1814     
1815     if (![self isStarted])
1816         return;
1817     
1818     if ([_manualStream plugin] == NULL) {
1819         [_manualStream setRequestURL:[[[self dataSource] request] URL]];
1820         [_manualStream setPlugin:[self plugin]];
1821         ASSERT([_manualStream plugin]);
1822         [_manualStream startStreamWithResponse:[[self dataSource] response]];
1823     }
1824     
1825     if ([_manualStream plugin])
1826         [_manualStream receivedData:data];
1827 }
1828
1829 - (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
1830 {
1831     ASSERT(_loadManually);
1832     
1833     [error retain];
1834     [_error release];
1835     _error = error;
1836     
1837     if (![self isStarted]) {
1838         return;
1839     }
1840     
1841     [_manualStream destroyStreamWithError:error];
1842 }
1843
1844 - (void)pluginViewFinishedLoading:(NSView *)pluginView 
1845 {
1846     ASSERT(_loadManually);
1847     ASSERT(_manualStream);
1848     
1849     if ([self isStarted])
1850         [_manualStream finishedLoading];
1851 }
1852
1853 @end
1854
1855 @implementation WebBaseNetscapePluginView (WebNPPCallbacks)
1856
1857 - (NSMutableURLRequest *)requestWithURLCString:(const char *)URLCString
1858 {
1859     if (!URLCString)
1860         return nil;
1861     
1862     CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, URLCString, kCFStringEncodingISOLatin1);
1863     ASSERT(string); // All strings should be representable in ISO Latin 1
1864     
1865     NSString *URLString = [(NSString *)string _web_stringByStrippingReturnCharacters];
1866     NSURL *URL = [NSURL _web_URLWithDataAsString:URLString relativeToURL:baseURL];
1867     CFRelease(string);
1868     if (!URL)
1869         return nil;
1870
1871     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
1872     Frame* frame = core([self webFrame]);
1873     if (!frame)
1874         return nil;
1875     [request _web_setHTTPReferrer:frame->loader()->outgoingReferrer()];
1876     return request;
1877 }
1878
1879 - (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest
1880 {
1881     // FIXME: Is this isStarted check needed here? evaluateJavaScriptPluginRequest should not be called
1882     // if we are stopped since this method is called after a delay and we call 
1883     // cancelPreviousPerformRequestsWithTarget inside of stop.
1884     if (!isStarted) {
1885         return;
1886     }
1887     
1888     NSURL *URL = [[JSPluginRequest request] URL];
1889     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1890     ASSERT(JSString);
1891     
1892     NSString *result = [[self webFrame] _stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]];
1893     
1894     // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop.
1895     if (!isStarted) {
1896         return;
1897     }
1898         
1899     if ([JSPluginRequest frameName] != nil) {
1900         // FIXME: If the result is a string, we probably want to put that string into the frame.
1901         if ([JSPluginRequest sendNotification]) {
1902             [self willCallPlugInFunction];
1903             {
1904                 KJS::JSLock::DropAllLocks dropAllLocks;
1905                 NPP_URLNotify(plugin, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]);
1906             }
1907             [self didCallPlugInFunction];
1908         }
1909     } else if ([result length] > 0) {
1910         // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does.
1911         NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding];
1912         WebBaseNetscapePluginStream *stream = [[WebBaseNetscapePluginStream alloc] initWithRequestURL:URL
1913                                                                                                plugin:plugin
1914                                                                                            notifyData:[JSPluginRequest notifyData]
1915                                                                                      sendNotification:[JSPluginRequest sendNotification]];
1916         [stream startStreamResponseURL:URL
1917                  expectedContentLength:[JSData length]
1918                       lastModifiedDate:nil
1919                               MIMEType:@"text/plain"
1920                                headers:nil];
1921         [stream receivedData:JSData];
1922         [stream finishedLoading];
1923         [stream release];
1924     }
1925 }
1926
1927 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
1928 {
1929     ASSERT(isStarted);
1930     
1931     WebPluginRequest *pluginRequest = [pendingFrameLoads objectForKey:webFrame];
1932     ASSERT(pluginRequest != nil);
1933     ASSERT([pluginRequest sendNotification]);
1934         
1935     [self willCallPlugInFunction];
1936     {
1937         KJS::JSLock::DropAllLocks dropAllLocks;
1938         NPP_URLNotify(plugin, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]);
1939     }
1940     [self didCallPlugInFunction];
1941     
1942     [pendingFrameLoads removeObjectForKey:webFrame];
1943     [webFrame _setInternalLoadDelegate:nil];
1944 }
1945
1946 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
1947 {
1948     NPReason reason = NPRES_DONE;
1949     if (error != nil) {
1950         reason = [WebBaseNetscapePluginStream reasonForError:error];
1951     }    
1952     [self webFrame:webFrame didFinishLoadWithReason:reason];
1953 }
1954
1955 - (void)loadPluginRequest:(WebPluginRequest *)pluginRequest
1956 {
1957     NSURLRequest *request = [pluginRequest request];
1958     NSString *frameName = [pluginRequest frameName];
1959     WebFrame *frame = nil;
1960     
1961     NSURL *URL = [request URL];
1962     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1963     
1964     ASSERT(frameName || JSString);
1965     
1966     if (frameName) {
1967         // FIXME - need to get rid of this window creation which
1968         // bypasses normal targeted link handling
1969         frame = kit(core([self webFrame])->loader()->findFrameForNavigation(frameName));
1970         if (frame == nil) {
1971             WebView *currentWebView = [self webView];
1972             NSDictionary *features = [[NSDictionary alloc] init];
1973             WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView
1974                                                         createWebViewWithRequest:nil
1975                                                                   windowFeatures:features];
1976             [features release];
1977
1978             if (!newWebView) {
1979                 if ([pluginRequest sendNotification]) {
1980                     [self willCallPlugInFunction];
1981                     {
1982                         KJS::JSLock::DropAllLocks dropAllLocks;
1983                         NPP_URLNotify(plugin, [[[pluginRequest request] URL] _web_URLCString], NPERR_GENERIC_ERROR, [pluginRequest notifyData]);
1984                     }
1985                     [self didCallPlugInFunction];
1986                 }
1987                 return;
1988             }
1989             
1990             frame = [newWebView mainFrame];
1991             core(frame)->tree()->setName(frameName);
1992             [[newWebView _UIDelegateForwarder] webViewShow:newWebView];
1993         }
1994     }
1995
1996     if (JSString) {
1997         ASSERT(frame == nil || [self webFrame] == frame);
1998         [self evaluateJavaScriptPluginRequest:pluginRequest];
1999     } else {
2000         [frame loadRequest:request];
2001         if ([pluginRequest sendNotification]) {
2002             // Check if another plug-in view or even this view is waiting for the frame to load.
2003             // If it is, tell it that the load was cancelled because it will be anyway.
2004             WebBaseNetscapePluginView *view = [frame _internalLoadDelegate];
2005             if (view != nil) {
2006                 ASSERT([view isKindOfClass:[WebBaseNetscapePluginView class]]);
2007                 [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK];
2008             }
2009             [pendingFrameLoads _webkit_setObject:pluginRequest forUncopiedKey:frame];
2010             [frame _setInternalLoadDelegate:self];
2011         }
2012     }
2013 }
2014
2015 - (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification
2016 {
2017     NSURL *URL = [request URL];
2018
2019     if (!URL) 
2020         return NPERR_INVALID_URL;
2021
2022     NSString *target = nil;
2023     if (cTarget) {
2024         // Find the frame given the target string.
2025         target = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, cTarget, kCFStringEncodingWindowsLatin1);
2026     }
2027     WebFrame *frame = [self webFrame];
2028
2029     // don't let a plugin start any loads if it is no longer part of a document that is being 
2030     // displayed unless the loads are in the same frame as the plugin.
2031     if ([[self dataSource] _documentLoader] != core([self webFrame])->loader()->activeDocumentLoader() &&
2032         (!cTarget || [frame findFrameNamed:target] != frame)) {
2033         if (target)
2034             CFRelease(target);
2035         return NPERR_GENERIC_ERROR; 
2036     }
2037     
2038     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
2039     if (JSString != nil) {
2040         if (![[[self webView] preferences] isJavaScriptEnabled]) {
2041             // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does.
2042             return NPERR_GENERIC_ERROR;
2043         } else if (cTarget == NULL && mode == NP_FULL) {
2044             // Don't allow a JavaScript request from a standalone plug-in that is self-targetted
2045             // because this can cause the user to be redirected to a blank page (3424039).
2046             return NPERR_INVALID_PARAM;
2047         }
2048     }
2049         
2050     if (cTarget || JSString) {
2051         // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't
2052         // want to potentially kill the plug-in inside of its URL request.
2053         
2054         if (JSString != nil && target != nil && [frame findFrameNamed:target] != frame) {
2055             // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
2056             CFRelease(target);
2057             return NPERR_INVALID_PARAM;
2058         }
2059         
2060         bool currentEventIsUserGesture = false;
2061         if (eventHandler)
2062             currentEventIsUserGesture = eventHandler->currentEventIsUserGesture();
2063         
2064         WebPluginRequest *pluginRequest = [[WebPluginRequest alloc] initWithRequest:request 
2065                                                                           frameName:target
2066                                                                          notifyData:notifyData 
2067                                                                    sendNotification:sendNotification
2068                                                             didStartFromUserGesture:currentEventIsUserGesture];
2069         [self performSelector:@selector(loadPluginRequest:) withObject:pluginRequest afterDelay:0];
2070         [pluginRequest release];
2071         if (target)
2072             CFRelease(target);
2073     } else {
2074         WebNetscapePluginStream *stream = [[WebNetscapePluginStream alloc] initWithRequest:request 
2075                                                                                     plugin:plugin 
2076                                                                                 notifyData:notifyData 
2077                                                                           sendNotification:sendNotification];
2078         if (!stream)
2079             return NPERR_INVALID_URL;
2080
2081         [streams addObject:stream];
2082         [stream start];
2083         [stream release];
2084     }
2085     
2086     return NPERR_NO_ERROR;
2087 }
2088
2089 -(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData
2090 {
2091     LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget);
2092
2093     NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
2094     return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES];
2095 }
2096
2097 -(NPError)getURL:(const char *)URLCString target:(const char *)cTarget
2098 {
2099     LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget);
2100
2101     NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
2102     return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO];
2103 }
2104
2105 - (NPError)_postURL:(const char *)URLCString
2106              target:(const char *)target
2107                 len:(UInt32)len
2108                 buf:(const char *)buf
2109                file:(NPBool)file
2110          notifyData:(void *)notifyData
2111    sendNotification:(BOOL)sendNotification
2112        allowHeaders:(BOOL)allowHeaders
2113 {
2114     if (!URLCString || !len || !buf) {
2115         return NPERR_INVALID_PARAM;
2116     }
2117     
2118     NSData *postData = nil;
2119
2120     if (file) {
2121         // If we're posting a file, buf is either a file URL or a path to the file.
2122         NSString *bufString = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingWindowsLatin1);
2123         if (!bufString) {
2124             return NPERR_INVALID_PARAM;
2125         }
2126         NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString];
2127         NSString *path;
2128         if ([fileURL isFileURL]) {
2129             path = [fileURL path];
2130         } else {
2131             path = bufString;
2132         }
2133         postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]];
2134         CFRelease(bufString);
2135         if (!postData) {
2136             return NPERR_FILE_NOT_FOUND;
2137         }
2138     } else {
2139         postData = [NSData dataWithBytes:buf length:len];
2140     }
2141
2142     if ([postData length] == 0) {
2143         return NPERR_INVALID_PARAM;
2144     }
2145
2146     NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
2147     [request setHTTPMethod:@"POST"];
2148     
2149     if (allowHeaders) {
2150         if ([postData _web_startsWithBlankLine]) {
2151             postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)];
2152         } else {
2153             NSInteger location = [postData _web_locationAfterFirstBlankLine];
2154             if (location != NSNotFound) {
2155                 // If the blank line is somewhere in the middle of postData, everything before is the header.
2156                 NSData *headerData = [postData subdataWithRange:NSMakeRange(0, location)];
2157                 NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields];
2158                 unsigned dataLength = [postData length] - location;
2159
2160                 // Sometimes plugins like to set Content-Length themselves when they post,
2161                 // but WebFoundation does not like that. So we will remove the header
2162                 // and instead truncate the data to the requested length.
2163                 NSString *contentLength = [header objectForKey:@"Content-Length"];
2164
2165                 if (contentLength != nil)
2166                     dataLength = MIN((unsigned)[contentLength intValue], dataLength);
2167                 [header removeObjectForKey:@"Content-Length"];
2168
2169                 if ([header count] > 0) {
2170                     [request setAllHTTPHeaderFields:header];
2171                 }
2172                 // Everything after the blank line is the actual content of the POST.
2173                 postData = [postData subdataWithRange:NSMakeRange(location, dataLength)];
2174
2175             }
2176         }
2177         if ([postData length] == 0) {
2178             return NPERR_INVALID_PARAM;
2179         }
2180     }
2181
2182     // Plug-ins expect to receive uncached data when doing a POST (3347134).
2183     [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
2184     [request setHTTPBody:postData];
2185     
2186     return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification];
2187 }
2188
2189 - (NPError)postURLNotify:(const char *)URLCString
2190                   target:(const char *)target
2191                      len:(UInt32)len
2192                      buf:(const char *)buf
2193                     file:(NPBool)file
2194               notifyData:(void *)notifyData
2195 {
2196     LOG(Plugins, "NPN_PostURLNotify: %s", URLCString);
2197     return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:notifyData sendNotification:YES allowHeaders:YES];
2198 }
2199
2200 -(NPError)postURL:(const char *)URLCString
2201            target:(const char *)target
2202               len:(UInt32)len
2203               buf:(const char *)buf
2204              file:(NPBool)file
2205 {
2206     LOG(Plugins, "NPN_PostURL: %s", URLCString);        
2207     // As documented, only allow headers to be specified via NPP_PostURL when using a file.
2208     return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:NULL sendNotification:NO allowHeaders:file];
2209 }
2210
2211 -(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream
2212 {
2213     LOG(Plugins, "NPN_NewStream");
2214     return NPERR_GENERIC_ERROR;
2215 }
2216
2217 -(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer
2218 {
2219     LOG(Plugins, "NPN_Write");
2220     return NPERR_GENERIC_ERROR;
2221 }
2222
2223 -(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason
2224 {
2225     LOG(Plugins, "NPN_DestroyStream");
2226     // This function does a sanity check to ensure that the NPStream provided actually
2227     // belongs to the plug-in that provided it, which fixes a crash in the DivX 
2228     // plug-in: <rdar://problem/5093862> | http://bugs.webkit.org/show_bug.cgi?id=13203
2229     if (!stream || [WebBaseNetscapePluginStream ownerForStream:stream] != plugin) {
2230         LOG(Plugins, "Invalid NPStream passed to NPN_DestroyStream: %p", stream);
2231         return NPERR_INVALID_INSTANCE_ERROR;
2232     }
2233     
2234     WebBaseNetscapePluginStream *browserStream = static_cast<WebBaseNetscapePluginStream *>(stream->ndata);
2235     [browserStream cancelLoadAndDestroyStreamWithError:[browserStream errorForReason:reason]];
2236     
2237     return NPERR_NO_ERROR;
2238 }
2239
2240 - (const char *)userAgent
2241 {
2242     return [[[self webView] userAgentForURL:baseURL] UTF8String];
2243 }
2244
2245 -(void)status:(const char *)message
2246 {    
2247     if (!message) {
2248         LOG_ERROR("NPN_Status passed a NULL status message");
2249         return;
2250     }
2251
2252     CFStringRef status = CFStringCreateWithCString(NULL, message, kCFStringEncodingUTF8);
2253     if (!status) {
2254         LOG_ERROR("NPN_Status: the message was not valid UTF-8");
2255         return;
2256     }
2257     
2258     LOG(Plugins, "NPN_Status: %@", status);
2259     WebView *wv = [self webView];
2260     [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status];
2261     CFRelease(status);
2262 }
2263
2264 -(void)invalidateRect:(NPRect *)invalidRect
2265 {
2266     LOG(Plugins, "NPN_InvalidateRect");
2267     [self setNeedsDisplayInRect:NSMakeRect(invalidRect->left, invalidRect->top,
2268         (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)];
2269 }
2270
2271 -(BOOL)isOpaque
2272 {
2273     return YES;
2274 }
2275
2276 - (void)invalidateRegion:(NPRegion)invalidRegion
2277 {
2278     LOG(Plugins, "NPN_InvalidateRegion");
2279     NSRect invalidRect = NSZeroRect;
2280     switch (drawingModel) {
2281 #ifndef NP_NO_QUICKDRAW
2282         case NPDrawingModelQuickDraw:
2283         {
2284             ::Rect qdRect;
2285             GetRegionBounds((NPQDRegion)invalidRegion, &qdRect);
2286             invalidRect = NSMakeRect(qdRect.left, qdRect.top, qdRect.right - qdRect.left, qdRect.bottom - qdRect.top);
2287         }
2288         break;
2289 #endif /* NP_NO_QUICKDRAW */
2290         
2291         case NPDrawingModelCoreGraphics:
2292         case NPDrawingModelOpenGL:
2293         {
2294             CGRect cgRect = CGPathGetBoundingBox((NPCGRegion)invalidRegion);
2295             invalidRect = *(NSRect *)&cgRect;
2296         }
2297         break;
2298     
2299         default:
2300             ASSERT_NOT_REACHED();
2301         break;
2302     }
2303     
2304     [self setNeedsDisplayInRect:invalidRect];
2305 }
2306
2307 -(void)forceRedraw
2308 {
2309     LOG(Plugins, "forceRedraw");
2310     [self setNeedsDisplay:YES];
2311     [[self window] displayIfNeeded];
2312 }
2313
2314 - (NPError)getVariable:(NPNVariable)variable value:(void *)value
2315 {
2316     switch (variable) {
2317         case NPNVWindowNPObject:
2318         {
2319             Frame* frame = core([self webFrame]);
2320             NPObject* windowScriptObject = frame ? frame->windowScriptNPObject() : 0;
2321
2322             // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
2323             if (windowScriptObject)
2324                 _NPN_RetainObject(windowScriptObject);
2325             
2326             void **v = (void **)value;
2327             *v = windowScriptObject;
2328
2329             return NPERR_NO_ERROR;
2330         }
2331
2332         case NPNVPluginElementNPObject:
2333         {
2334             if (!element)
2335                 return NPERR_GENERIC_ERROR;
2336             
2337             NPObject *plugInScriptObject = (NPObject *)[element _NPObject];
2338
2339             // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
2340             if (plugInScriptObject)
2341                 _NPN_RetainObject(plugInScriptObject);
2342
2343             void **v = (void **)value;
2344             *v = plugInScriptObject;
2345
2346             return NPERR_NO_ERROR;
2347         }
2348         
2349         case NPNVpluginDrawingModel:
2350         {
2351             *(NPDrawingModel *)value = drawingModel;
2352             return NPERR_NO_ERROR;
2353         }
2354
2355 #ifndef NP_NO_QUICKDRAW
2356         case NPNVsupportsQuickDrawBool:
2357         {
2358             *(NPBool *)value = TRUE;
2359             return NPERR_NO_ERROR;
2360         }
2361 #endif /* NP_NO_QUICKDRAW */
2362         
2363         case NPNVsupportsCoreGraphicsBool:
2364         {
2365             *(NPBool *)value = TRUE;
2366             return NPERR_NO_ERROR;
2367         }
2368
2369         case NPNVsupportsOpenGLBool:
2370         {
2371             *(NPBool *)value = TRUE;
2372             return NPERR_NO_ERROR;
2373         }
2374         
2375         case NPNVpluginEventModel:
2376         {
2377             *(NPEventModel *)value = eventModel;
2378             return NPERR_NO_ERROR;
2379         }
2380         
2381 #ifndef NP_NO_CARBON
2382         case NPNVsupportsCarbonBool:
2383         {
2384             *(NPBool *)value = TRUE;
2385             return NPERR_NO_ERROR;
2386         }
2387 #endif /* NP_NO_CARBON */
2388             
2389         case NPNVsupportsCocoaBool:
2390         {
2391             *(NPBool *)value = TRUE;
2392             return NPERR_NO_ERROR;
2393         }
2394             
2395         default:
2396             break;
2397     }
2398
2399     return NPERR_GENERIC_ERROR;
2400 }
2401
2402 - (NPError)setVariable:(NPPVariable)variable value:(void *)value
2403 {
2404     switch (variable) {
2405         case NPPVpluginWindowBool:
2406         {
2407             NPWindowType newWindowType = (value ? NPWindowTypeWindow : NPWindowTypeDrawable);
2408
2409             // Redisplay if window type is changing (some drawing models can only have their windows set while updating).
2410             if (newWindowType != window.type)
2411                 [self setNeedsDisplay:YES];
2412             
2413             window.type = newWindowType;
2414         }
2415         
2416         case NPPVpluginTransparentBool:
2417         {
2418             BOOL newTransparent = (value != 0);
2419             
2420             // Redisplay if transparency is changing
2421             if (isTransparent != newTransparent)
2422                 [self setNeedsDisplay:YES];
2423             
2424             isTransparent = newTransparent;
2425             
2426             return NPERR_NO_ERROR;
2427         }
2428         
2429         case NPPVpluginDrawingModel:
2430         {
2431             // Can only set drawing model inside NPP_New()
2432             if (self != [[self class] currentPluginView])
2433                 return NPERR_GENERIC_ERROR;
2434             
2435             // Check for valid, supported drawing model
2436             NPDrawingModel newDrawingModel = (NPDrawingModel)(uintptr_t)value;
2437             switch (newDrawingModel) {
2438                 // Supported drawing models:
2439 #ifndef NP_NO_QUICKDRAW
2440                 case NPDrawingModelQuickDraw:
2441 #endif
2442                 case NPDrawingModelCoreGraphics:
2443                 case NPDrawingModelOpenGL:
2444                     drawingModel = newDrawingModel;
2445                     return NPERR_NO_ERROR;
2446                 
2447                 // Unsupported (or unknown) drawing models:
2448                 default:
2449                     LOG(Plugins, "Plugin %@ uses unsupported drawing model: %d", pluginPackage, drawingModel);
2450                     return NPERR_GENERIC_ERROR;
2451             }
2452         }
2453         
2454         case NPPVpluginEventModel:
2455         {
2456             // Can only set event model inside NPP_New()
2457             if (self != [[self class] currentPluginView])
2458                 return NPERR_GENERIC_ERROR;
2459             
2460             // Check for valid, supported event model
2461             NPEventModel newEventModel = (NPEventModel)(uintptr_t)value;
2462             switch (newEventModel) {
2463                 // Supported event models:
2464 #ifndef NP_NO_CARBON
2465                 case NPEventModelCarbon:
2466 #endif
2467                 case NPEventModelCocoa:
2468                     eventModel = newEventModel;
2469                     return NPERR_NO_ERROR;
2470                     
2471                     // Unsupported (or unknown) event models:
2472                 default:
2473                     LOG(Plugins, "Plugin %@ uses unsupported event model: %d", pluginPackage, eventModel);
2474                     return NPERR_GENERIC_ERROR;
2475             }
2476         }
2477             
2478         default:
2479             return NPERR_GENERIC_ERROR;
2480     }
2481 }
2482
2483 - (uint32)scheduleTimerWithInterval:(uint32)interval repeat:(NPBool)repeat timerFunc:(void (*)(NPP npp, uint32 timerID))timerFunc
2484 {
2485     if (!timerFunc)
2486         return 0;
2487     
2488     if (!timers)
2489         timers = new HashMap<uint32, PluginTimer*>;
2490     
2491     uint32 timerID = ++currentTimerID;
2492     
2493     PluginTimer* timer = new PluginTimer(plugin, timerID, interval, repeat, timerFunc);
2494     timers->set(timerID, timer);
2495
2496     if (shouldFireTimers)
2497         timer->start(isCompletelyObscured);
2498     
2499     return 0;
2500 }
2501
2502 - (void)unscheduleTimer:(uint32)timerID
2503 {
2504     if (!timers)
2505         return;
2506     
2507     if (PluginTimer* timer = timers->take(timerID))
2508         delete timer;
2509 }
2510
2511 - (NPError)popUpContextMenu:(NPMenu *)menu
2512 {
2513     NSEvent *currentEvent = [NSApp currentEvent];
2514     
2515     // NPN_PopUpContextMenu must be called from within the plug-in's NPP_HandleEvent.
2516     if (!currentEvent)
2517         return NPERR_GENERIC_ERROR;
2518     
2519     [NSMenu popUpContextMenu:(NSMenu *)menu withEvent:currentEvent forView:self];
2520     return NPERR_NO_ERROR;
2521 }
2522
2523 @end
2524
2525 @implementation WebPluginRequest
2526
2527 - (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture
2528 {
2529     [super init];
2530     _didStartFromUserGesture = currentEventIsUserGesture;
2531     _request = [request retain];
2532     _frameName = [frameName retain];
2533     _notifyData = notifyData;
2534     _sendNotification = sendNotification;
2535     return self;
2536 }
2537
2538 - (void)dealloc
2539 {
2540     [_request release];
2541     [_frameName release];
2542     [super dealloc];
2543 }
2544
2545 - (NSURLRequest *)request
2546 {
2547     return _request;
2548 }
2549
2550 - (NSString *)frameName
2551 {
2552     return _frameName;
2553 }
2554
2555 - (BOOL)isCurrentEventUserGesture
2556 {
2557     return _didStartFromUserGesture;
2558 }
2559
2560 - (BOOL)sendNotification
2561 {
2562     return _sendNotification;
2563 }
2564
2565 - (void *)notifyData
2566 {
2567     return _notifyData;
2568 }
2569
2570 @end
2571
2572 @implementation WebBaseNetscapePluginView (Internal)
2573
2574 - (NPError)_createPlugin
2575 {
2576     plugin = (NPP)calloc(1, sizeof(NPP_t));
2577     plugin->ndata = self;
2578
2579     ASSERT(NPP_New);
2580
2581     // NPN_New(), which creates the plug-in instance, should never be called while calling a plug-in function for that instance.
2582     ASSERT(pluginFunctionCallDepth == 0);
2583
2584     [[self class] setCurrentPluginView:self];
2585     NPError npErr = NPP_New((char *)[MIMEType cString], plugin, mode, argsCount, cAttributes, cValues, NULL);
2586     [[self class] setCurrentPluginView:nil];
2587     
2588     LOG(Plugins, "NPP_New: %d", npErr);
2589     return npErr;
2590 }
2591
2592 - (void)_destroyPlugin
2593 {
2594     NPError npErr;
2595     npErr = NPP_Destroy(plugin, NULL);
2596     LOG(Plugins, "NPP_Destroy: %d", npErr);
2597     
2598     if (Frame* frame = core([self webFrame]))
2599         frame->cleanupScriptObjectsForPlugin(self);
2600         
2601     free(plugin);
2602     plugin = NULL;
2603 }
2604
2605 - (void)_viewHasMoved
2606 {
2607     // All of the work this method does may safely be skipped if the view is not in a window.  When the view
2608     // is moved back into a window, everything should be set up correctly.
2609     if (![self window])
2610         return;
2611     
2612     if (drawingModel == NPDrawingModelOpenGL)
2613         [self _reshapeAGLWindow];
2614
2615 #ifndef NP_NO_QUICKDRAW
2616     if (drawingModel == NPDrawingModelQuickDraw)
2617         [self tellQuickTimeToChill];
2618 #endif
2619     [self updateAndSetWindow];
2620     [self resetTrackingRect];
2621     
2622     // Check to see if the plugin view is completely obscured (scrolled out of view, for example).
2623     // For performance reasons, we send null events at a lower rate to plugins which are obscured.
2624     BOOL oldIsObscured = isCompletelyObscured;
2625     isCompletelyObscured = NSIsEmptyRect([self visibleRect]);
2626     if (isCompletelyObscured != oldIsObscured)
2627         [self restartTimers];
2628 }
2629
2630 - (NSBitmapImageRep *)_printedPluginBitmap
2631 {
2632 #ifdef NP_NO_QUICKDRAW
2633     return nil;
2634 #else
2635     // Cannot print plugins that do not implement NPP_Print
2636     if (!NPP_Print)
2637         return nil;
2638
2639     // This NSBitmapImageRep will share its bitmap buffer with a GWorld that the plugin will draw into.
2640     // The bitmap is created in 32-bits-per-pixel ARGB format, which is the default GWorld pixel format.
2641     NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
2642                                                          pixelsWide:window.width
2643                                                          pixelsHigh:window.height
2644                                                          bitsPerSample:8
2645                                                          samplesPerPixel:4
2646                                                          hasAlpha:YES
2647                                                          isPlanar:NO
2648                                                          colorSpaceName:NSDeviceRGBColorSpace
2649                                                          bitmapFormat:NSAlphaFirstBitmapFormat
2650                                                          bytesPerRow:0
2651                                                          bitsPerPixel:0] autorelease];
2652     ASSERT(bitmap);
2653     
2654     // Create a GWorld with the same underlying buffer into which the plugin can draw
2655     ::Rect printGWorldBounds;
2656     SetRect(&printGWorldBounds, 0, 0, window.width, window.height);
2657     GWorldPtr printGWorld;
2658     if (NewGWorldFromPtr(&printGWorld,
2659                          k32ARGBPixelFormat,
2660                          &printGWorldBounds,
2661                          NULL,
2662                          NULL,
2663                          0,
2664                          (Ptr)[bitmap bitmapData],
2665                          [bitmap bytesPerRow]) != noErr) {
2666         LOG_ERROR("Could not create GWorld for printing");
2667         return nil;
2668     }
2669     
2670     /// Create NPWindow for the GWorld
2671     NPWindow printNPWindow;
2672     printNPWindow.window = &printGWorld; // Normally this is an NP_Port, but when printing it is the actual CGrafPtr
2673     printNPWindow.x = 0;
2674     printNPWindow.y = 0;
2675     printNPWindow.width = window.width;
2676     printNPWindow.height = window.height;
2677     printNPWindow.clipRect.top = 0;
2678     printNPWindow.clipRect.left = 0;
2679     printNPWindow.clipRect.right = window.width;
2680     printNPWindow.clipRect.bottom = window.height;
2681     printNPWindow.type = NPWindowTypeDrawable; // Offscreen graphics port as opposed to a proper window
2682     
2683     // Create embed-mode NPPrint
2684     NPPrint npPrint;
2685     npPrint.mode = NP_EMBED;
2686     npPrint.print.embedPrint.window = printNPWindow;
2687     npPrint.print.embedPrint.platformPrint = printGWorld;
2688     
2689     // Tell the plugin to print into the GWorld
2690     [self willCallPlugInFunction];
2691     {
2692         KJS::JSLock::DropAllLocks dropAllLocks;
2693         NPP_Print(plugin, &npPrint);
2694     }
2695     [self didCallPlugInFunction];
2696
2697     // Don't need the GWorld anymore
2698     DisposeGWorld(printGWorld);
2699         
2700     return bitmap;
2701 #endif
2702 }
2703
2704 - (BOOL)_createAGLContextIfNeeded
2705 {
2706     ASSERT(drawingModel == NPDrawingModelOpenGL);
2707
2708     // Do nothing (but indicate success) if the AGL context already exists
2709     if (aglContext)
2710         return YES;
2711         
2712     switch (window.type) {
2713         case NPWindowTypeWindow:
2714             return [self _createWindowedAGLContext];
2715         
2716         case NPWindowTypeDrawable:
2717             return [self _createWindowlessAGLContext];
2718         
2719         default:
2720             ASSERT_NOT_REACHED();
2721             return NO;
2722     }
2723 }
2724
2725 - (BOOL)_createWindowedAGLContext
2726 {
2727     ASSERT(drawingModel == NPDrawingModelOpenGL);
2728     ASSERT(!aglContext);
2729     ASSERT(!aglWindow);
2730     ASSERT([self window]);
2731     
2732     GLint pixelFormatAttributes[] = {
2733         AGL_RGBA,
2734         AGL_RED_SIZE, 8,
2735         AGL_GREEN_SIZE, 8,
2736         AGL_BLUE_SIZE, 8,
2737         AGL_ALPHA_SIZE, 8,
2738         AGL_DEPTH_SIZE, 32,
2739         AGL_WINDOW,
2740         AGL_ACCELERATED,
2741         0
2742     };
2743     
2744     // Choose AGL pixel format
2745     AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL, 0, pixelFormatAttributes);
2746     if (!pixelFormat) {
2747         LOG_ERROR("Could not find suitable AGL pixel format: %s", aglErrorString(aglGetError()));
2748         return NO;
2749     }
2750     
2751     // Create AGL context
2752     aglContext = aglCreateContext(pixelFormat, NULL);
2753     aglDestroyPixelFormat(pixelFormat);
2754     if (!aglContext) {
2755         LOG_ERROR("Could not create AGL context: %s", aglErrorString(aglGetError()));
2756         return NO;
2757     }
2758     
2759     // Create AGL window
2760     aglWindow = [[NSWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
2761     if (!aglWindow) {
2762         LOG_ERROR("Could not create window for AGL drawable.");
2763         return NO;
2764     }
2765     
2766     // AGL window should allow clicks to go through -- mouse events are tracked by WebCore
2767     [aglWindow setIgnoresMouseEvents:YES];
2768     
2769     // Make sure the window is not opaque -- windowed plug-ins cannot layer with other page elements
2770     [aglWindow setOpaque:YES];
2771
2772     // Position and order in the AGL window
2773     [self _reshapeAGLWindow];
2774
2775     // Attach the AGL context to its window
2776     GLboolean success;
2777 #ifdef AGL_VERSION_3_0
2778     success = aglSetWindowRef(aglContext, (WindowRef)[aglWindow windowRef]);
2779 #else
2780     success = aglSetDrawable(aglContext, (AGLDrawable)GetWindowPort((WindowRef)[aglWindow windowRef]));
2781 #endif
2782     if (!success) {
2783         LOG_ERROR("Could not set AGL drawable: %s", aglErrorString(aglGetError()));
2784         aglDestroyContext(aglContext);
2785         aglContext = NULL;
2786         return NO;
2787     }
2788         
2789     return YES;
2790 }
2791
2792 - (BOOL)_createWindowlessAGLContext
2793 {
2794     ASSERT(drawingModel == NPDrawingModelOpenGL);
2795     ASSERT(!aglContext);
2796     ASSERT(!aglWindow);
2797     
2798     GLint pixelFormatAttributes[] = {
2799         AGL_RGBA,
2800         AGL_RED_SIZE, 8,
2801         AGL_GREEN_SIZE, 8,
2802         AGL_BLUE_SIZE, 8,
2803         AGL_ALPHA_SIZE, 8,
2804         AGL_DEPTH_SIZE, 32,
2805         AGL_OFFSCREEN,
2806         0
2807     };
2808
2809     // Choose AGL pixel format
2810     AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL, 0, pixelFormatAttributes);
2811     if (!pixelFormat) {
2812         LOG_ERROR("Could not find suitable AGL pixel format: %s", aglErrorString(aglGetError()));
2813         return NO;
2814     }
2815     
2816     // Create AGL context
2817     aglContext = aglCreateContext(pixelFormat, NULL);
2818     aglDestroyPixelFormat(pixelFormat);
2819     if (!aglContext) {
2820         LOG_ERROR("Could not create AGL context: %s", aglErrorString(aglGetError()));
2821         return NO;
2822     }
2823     
2824     // Create offscreen buffer for AGL context
2825     NSSize boundsSize = [self bounds].size;
2826     GLvoid *offscreenBuffer = (GLvoid *)malloc(static_cast<size_t>(boundsSize.width * boundsSize.height * 4));
2827     if (!offscreenBuffer) {
2828         LOG_ERROR("Could not allocate offscreen buffer for AGL context");
2829         aglDestroyContext(aglContext);
2830         aglContext = NULL;
2831         return NO;
2832     }
2833     
2834     // Attach AGL context to offscreen buffer
2835     CGLContextObj cglContext = [self _cglContext];
2836     CGLError error = CGLSetOffScreen(cglContext, static_cast<long>(boundsSize.width), static_cast<long>(boundsSize.height), static_cast<long>(boundsSize.width * 4), offscreenBuffer);
2837     if (error) {
2838         LOG_ERROR("Could not set offscreen buffer for AGL context: %d", error);
2839         aglDestroyContext(aglContext);
2840         aglContext = NULL;
2841         return NO;
2842     }
2843     
2844     return YES;
2845 }
2846
2847 - (CGLContextObj)_cglContext
2848 {
2849     ASSERT(drawingModel == NPDrawingModelOpenGL);
2850
2851     CGLContextObj cglContext = NULL;
2852     if (!aglGetCGLContext(aglContext, (void **)&cglContext) || !cglContext)
2853         LOG_ERROR("Could not get CGL context for AGL context: %s", aglErrorString(aglGetError()));
2854         
2855     return cglContext;
2856 }
2857
2858 - (BOOL)_getAGLOffscreenBuffer:(GLvoid **)outBuffer width:(GLsizei *)outWidth height:(GLsizei *)outHeight
2859 {
2860     ASSERT(drawingModel == NPDrawingModelOpenGL);
2861     
2862     if (outBuffer)
2863         *outBuffer = NULL;
2864     if (outWidth)
2865         *outWidth = 0;
2866     if (outHeight)
2867         *outHeight = 0;
2868     
2869     // Only windowless plug-ins have offscreen buffers
2870     if (window.type != NPWindowTypeDrawable)
2871         return NO;
2872     
2873     CGLContextObj cglContext = [self _cglContext];
2874     if (!cglContext)
2875         return NO;
2876     
2877     GLsizei width, height;
2878     GLint rowBytes;
2879     void *offscreenBuffer = NULL;
2880     CGLError error = CGLGetOffScreen(cglContext, &width, &height, &rowBytes, &offscreenBuffer);
2881     if (error || !offscreenBuffer) {
2882         LOG_ERROR("Could not get offscreen buffer for AGL context: %d", error);
2883         return NO;
2884     }
2885     
2886     if (outBuffer)
2887         *outBuffer = offscreenBuffer;
2888     if (outWidth)
2889         *outWidth = width;
2890     if (outHeight)
2891         *outHeight = height;
2892     
2893     return YES;
2894 }
2895
2896 - (void)_destroyAGLContext
2897 {    
2898     ASSERT(drawingModel == NPDrawingModelOpenGL);
2899
2900     if (!aglContext)
2901         return;
2902
2903     if (aglContext) {
2904         // If this is a windowless plug-in, free its offscreen buffer
2905         GLvoid *offscreenBuffer;
2906         if ([self _getAGLOffscreenBuffer:&offscreenBuffer width:NULL height:NULL])
2907             free(offscreenBuffer);
2908         
2909         // Detach context from the AGL window
2910 #ifdef AGL_VERSION_3_0
2911         aglSetWindowRef(aglContext, NULL);
2912 #else
2913         aglSetDrawable(aglContext, NULL);
2914 #endif
2915         
2916         // Destroy the context
2917         aglDestroyContext(aglContext);
2918         aglContext = NULL;
2919     }
2920     
2921     // Destroy the AGL window
2922     if (aglWindow) {
2923         [self _hideAGLWindow];
2924         aglWindow = nil;
2925     }
2926 }
2927
2928 - (void)_reshapeAGLWindow
2929 {
2930     ASSERT(drawingModel == NPDrawingModelOpenGL);
2931     
2932     if (!aglContext)
2933         return;
2934
2935     switch (window.type) {
2936         case NPWindowTypeWindow:
2937         {
2938             if (!aglWindow)
2939                 break;
2940                 
2941             // The AGL window is being reshaped because the plugin view has moved.  Since the view has moved, it will soon redraw.
2942             // We want the AGL window to update at the same time as its underlying view.  So, we disable screen updates until the
2943             // plugin view's window flushes.
2944             NSWindow *browserWindow = [self window];
2945             ASSERT(browserWindow);
2946             [browserWindow disableScreenUpdatesUntilFlush];
2947
2948             // Add the AGL window as a child of the main window if necessary
2949             if ([aglWindow parentWindow] != browserWindow)
2950                 [browserWindow addChildWindow:aglWindow ordered:NSWindowAbove];
2951             
2952             // Update the AGL window frame
2953             NSRect aglWindowFrame = [self convertRect:[self visibleRect] toView:nil];
2954             aglWindowFrame.origin = [browserWindow convertBaseToScreen:aglWindowFrame.origin];
2955             [aglWindow setFrame:aglWindowFrame display:NO];
2956             
2957             // Update the AGL context
2958             aglUpdateContext(aglContext);
2959         }
2960         break;
2961         
2962         case NPWindowTypeDrawable:
2963         {
2964             // Get offscreen buffer; we can skip this step if we don't have one yet
2965             GLvoid *offscreenBuffer;
2966             GLsizei width, height;
2967             if (![self _getAGLOffscreenBuffer:&offscreenBuffer width:&width height:&height] || !offscreenBuffer)
2968                 break;
2969             
2970             // Don't resize the offscreen buffer if it's already the same size as the view bounds
2971             NSSize boundsSize = [self bounds].size;
2972             if (boundsSize.width == width && boundsSize.height == height)
2973                 break;
2974             
2975             // Resize the offscreen buffer
2976             offscreenBuffer = realloc(offscreenBuffer, static_cast<size_t>(boundsSize.width * boundsSize.height * 4));
2977             if (!offscreenBuffer) {
2978                 LOG_ERROR("Could not allocate offscreen buffer for AGL context");
2979                 break;
2980             }
2981
2982             // Update the offscreen 
2983             CGLContextObj cglContext = [self _cglContext];
2984             CGLError error = CGLSetOffScreen(cglContext, static_cast<long>(boundsSize.width), static_cast<long>(boundsSize.height), static_cast<long>(boundsSize.width * 4), offscreenBuffer);
2985             if (error) {
2986                 LOG_ERROR("Could not set offscreen buffer for AGL context: %d", error);
2987                 break;
2988             }
2989
2990             // Update the AGL context
2991             aglUpdateContext(aglContext);
2992         }
2993         break;
2994         
2995         default:
2996             ASSERT_NOT_REACHED();
2997         break;
2998     }
2999 }
3000
3001 - (void)_hideAGLWindow
3002 {
3003     ASSERT(drawingModel == NPDrawingModelOpenGL);
3004     
3005     if (!aglWindow)
3006         return;
3007     
3008     // aglWindow should only be set for a windowed OpenGL plug-in
3009     ASSERT(window.type == NPWindowTypeWindow);
3010     
3011     NSWindow *parentWindow = [aglWindow parentWindow];
3012     if (parentWindow) {
3013         // Disable screen updates so that this AGL window orders out atomically with other plugins' AGL windows
3014         [parentWindow disableScreenUpdatesUntilFlush];
3015         ASSERT(parentWindow == [self window]);
3016         [parentWindow removeChildWindow:aglWindow];
3017     }
3018     [aglWindow orderOut:nil];
3019 }
3020
3021 - (NSImage *)_aglOffscreenImageForDrawingInRect:(NSRect)drawingInRect
3022 {
3023     ASSERT(drawingModel == NPDrawingModelOpenGL);
3024
3025     CGLContextObj cglContext = [self _cglContext];
3026     if (!cglContext)
3027         return nil;
3028
3029     // Get the offscreen buffer
3030     GLvoid *offscreenBuffer;
3031     GLsizei width, height;
3032     if (![self _getAGLOffscreenBuffer:&offscreenBuffer width:&width height:&height])
3033         return nil;
3034
3035     unsigned char *plane = (unsigned char *)offscreenBuffer;
3036
3037 #if defined(__i386__) || defined(__x86_64__)
3038     // Make rect inside the offscreen buffer because we're about to directly modify the bits inside drawingInRect
3039     NSRect rect = NSIntegralRect(NSIntersectionRect(drawingInRect, NSMakeRect(0, 0, width, height)));
3040
3041     // The offscreen buffer, being an OpenGL framebuffer, is in BGRA format on x86.  We need to swap the blue and red channels before
3042     // wrapping the buffer in an NSBitmapImageRep, which only supports RGBA and ARGB.
3043     // On PowerPC, the OpenGL framebuffer is in ARGB format.  Since that is a format that NSBitmapImageRep supports, all that is
3044     // needed on PowerPC is to pass the NSAlphaFirstBitmapFormat flag when creating the NSBitmapImageRep.  On x86, we need to swap the
3045     // framebuffer color components such that they are in ARGB order, as they are on PowerPC.
3046     // If only a small region of the plug-in is being redrawn, then it would be a waste to convert the entire image from BGRA to ARGB.
3047     // Since we know what region of the image will ultimately be drawn to screen (drawingInRect), we restrict the channel swapping to
3048     // just that region within the offscreen buffer.
3049     if (!WebConvertBGRAToARGB(plane, width * 4, (int)rect.origin.x, (int)rect.origin.y, (int)rect.size.width, (int)rect.size.height))
3050         return nil;
3051 #endif /* defined(__i386__) || defined(__x86_64__) */
3052     
3053     NSBitmapImageRep *aglBitmap = [[NSBitmapImageRep alloc]
3054         initWithBitmapDataPlanes:&plane
3055                       pixelsWide:width
3056                       pixelsHigh:height
3057                    bitsPerSample:8
3058                  samplesPerPixel:4
3059                         hasAlpha:YES
3060                         isPlanar:NO
3061                   colorSpaceName:NSDeviceRGBColorSpace
3062                     bitmapFormat:NSAlphaFirstBitmapFormat
3063                      bytesPerRow:width * 4
3064                     bitsPerPixel:32];
3065     if (!aglBitmap) {
3066         LOG_ERROR("Could not create bitmap for AGL offscreen buffer");
3067         return nil;
3068     }
3069
3070     // Wrap the bitmap in an NSImage.  This allocation isn't very expensive -- the actual image data is already in the bitmap rep
3071     NSImage *aglImage = [[[NSImage alloc] initWithSize:[aglBitmap size]] autorelease];
3072     [aglImage addRepresentation:aglBitmap];
3073     [aglBitmap release];
3074     
3075     return aglImage;
3076 }
3077
3078 - (void)_redeliverStream
3079 {
3080     if ([self dataSource] && [self isStarted]) {
3081         // Deliver what has not been passed to the plug-in up to this point.
3082         if (_dataLengthReceived > 0) {
3083             NSData *data = [[[self dataSource] data] subdataWithRange:NSMakeRange(0, _dataLengthReceived)];
3084             _dataLengthReceived = 0;
3085             [self pluginView:self receivedData:data];
3086             if (![[self dataSource] isLoading]) {
3087                 if (_error)
3088                     [self pluginView:self receivedError:_error];
3089                 else
3090                     [self pluginViewFinishedLoading:self];
3091             }
3092         }
3093     }
3094 }
3095
3096 @end
3097
3098 @implementation NSData (PluginExtras)
3099
3100 - (BOOL)_web_startsWithBlankLine
3101 {
3102     return [self length] > 0 && ((const char *)[self bytes])[0] == '\n';
3103 }
3104
3105
3106 - (NSInteger)_web_locationAfterFirstBlankLine
3107 {
3108     const char *bytes = (const char *)[self bytes];
3109     unsigned length = [self length];
3110     
3111     unsigned i;
3112     for (i = 0; i < length - 4; i++) {
3113         
3114         //  Support for Acrobat. It sends "\n\n".
3115         if (bytes[i] == '\n' && bytes[i+1] == '\n') {
3116             return i+2;
3117         }
3118         
3119         // Returns the position after 2 CRLF's or 1 CRLF if it is the first line.
3120         if (bytes[i] == '\r' && bytes[i+1] == '\n') {
3121             i += 2;
3122             if (i == 2) {
3123                 return i;
3124             } else if (bytes[i] == '\n') {
3125                 // Support for Director. It sends "\r\n\n" (3880387).
3126                 return i+1;
3127             } else if (bytes[i] == '\r' && bytes[i+1] == '\n') {
3128                 // Support for Flash. It sends "\r\n\r\n" (3758113).
3129                 return i+2;
3130             }
3131         }
3132     }
3133     return NSNotFound;
3134 }
3135
3136 @end
3137 #endif