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