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