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