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