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