webengine/osswebengine/WebKit/Plugins/WebBaseNetscapePluginView.mm
changeset 0 dd21522fd290
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/WebKit/Plugins/WebBaseNetscapePluginView.mm	Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,3203 @@
+/*
+ * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer. 
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __LP64__
+
+#import "WebBaseNetscapePluginView.h"
+
+#import "WebDataSourceInternal.h"
+#import "WebDefaultUIDelegate.h"
+#import "WebFrameBridge.h"
+#import "WebFrameInternal.h" 
+#import "WebFrameView.h"
+#import "WebGraphicsExtras.h"
+#import "WebKitLogging.h"
+#import "WebKitNSStringExtras.h"
+#import "WebKitSystemInterface.h"
+#import "WebNSDataExtras.h"
+#import "WebNSDictionaryExtras.h"
+#import "WebNSObjectExtras.h"
+#import "WebNSURLExtras.h"
+#import "WebNSURLRequestExtras.h"
+#import "WebNSViewExtras.h"
+#import "WebNetscapePluginPackage.h"
+#import "WebNetscapePluginStream.h"
+#import "WebNullPluginView.h"
+#import "WebPreferences.h"
+#import "WebViewInternal.h"
+#import <Carbon/Carbon.h>
+#import <JavaScriptCore/Assertions.h>
+#import <JavaScriptCore/JSLock.h>
+#import <JavaScriptCore/npruntime_impl.h>
+#import <WebCore/Document.h>
+#import <WebCore/Element.h>
+#import <WebCore/Frame.h> 
+#import <WebCore/FrameLoader.h> 
+#import <WebCore/FrameTree.h> 
+#import <WebCore/Page.h> 
+#import <WebCore/SoftLinking.h> 
+#import <WebCore/WebCoreObjCExtras.h>
+#import <WebKit/DOMPrivate.h>
+#import <WebKit/WebUIDelegate.h>
+#import <objc/objc-runtime.h>
+
+using namespace WebCore;
+
+// Send null events 50 times a second when active, so plug-ins like Flash get high frame rates.
+#define NullEventIntervalActive         0.02
+#define NullEventIntervalNotActive      0.25
+
+#define LoginWindowDidSwitchFromUserNotification    @"WebLoginWindowDidSwitchFromUserNotification"
+#define LoginWindowDidSwitchToUserNotification      @"WebLoginWindowDidSwitchToUserNotification"
+
+SOFT_LINK_FRAMEWORK(OpenGL)
+SOFT_LINK_FRAMEWORK(AGL)
+
+SOFT_LINK(OpenGL, CGLGetOffScreen, CGLError, (CGLContextObj ctx, GLsizei *width, GLsizei *height, GLint *rowbytes, void **baseaddr), (ctx, width, height, rowbytes, baseaddr))
+SOFT_LINK(OpenGL, CGLSetOffScreen, CGLError, (CGLContextObj ctx, GLsizei width, GLsizei height, GLint rowbytes, void *baseaddr), (ctx, width, height, rowbytes, baseaddr))
+SOFT_LINK(OpenGL, glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height), (x, y, width, height))
+SOFT_LINK(AGL, aglCreateContext, AGLContext, (AGLPixelFormat pix, AGLContext share), (pix, share))
+SOFT_LINK(AGL, aglSetWindowRef, GLboolean, (AGLContext ctx, WindowRef window), (ctx, window))
+SOFT_LINK(AGL, aglSetDrawable, GLboolean, (AGLContext ctx, AGLDrawable draw), (ctx, draw))
+#ifndef BUILDING_ON_TIGER
+SOFT_LINK(AGL, aglChoosePixelFormat, AGLPixelFormat, (const void *gdevs, GLint ndev, const GLint *attribs), (gdevs, ndev, attribs))
+#else
+SOFT_LINK(AGL, aglChoosePixelFormat, AGLPixelFormat, (const AGLDevice *gdevs, GLint ndev, const GLint *attribs), (gdevs, ndev, attribs))
+#endif
+SOFT_LINK(AGL, aglDestroyPixelFormat, void, (AGLPixelFormat pix), (pix))
+SOFT_LINK(AGL, aglDestroyContext, GLboolean, (AGLContext ctx), (ctx))
+SOFT_LINK(AGL, aglGetCGLContext, GLboolean, (AGLContext ctx, void **cgl_ctx), (ctx, cgl_ctx))
+SOFT_LINK(AGL, aglGetCurrentContext, AGLContext, (void), ())
+SOFT_LINK(AGL, aglSetCurrentContext, GLboolean, (AGLContext ctx), (ctx))
+SOFT_LINK(AGL, aglGetError, GLenum, (void), ())
+SOFT_LINK(AGL, aglUpdateContext, GLboolean, (AGLContext ctx), (ctx))
+SOFT_LINK(AGL, aglErrorString, const GLubyte *, (GLenum code), (code))
+
+@interface WebBaseNetscapePluginView (Internal)
+- (void)_viewHasMoved;
+- (NPError)_createPlugin;
+- (void)_destroyPlugin;
+- (NSBitmapImageRep *)_printedPluginBitmap;
+- (BOOL)_createAGLContextIfNeeded;
+- (BOOL)_createWindowedAGLContext;
+- (BOOL)_createWindowlessAGLContext;
+- (CGLContextObj)_cglContext;
+- (BOOL)_getAGLOffscreenBuffer:(GLvoid **)outBuffer width:(GLsizei *)outWidth height:(GLsizei *)outHeight;
+- (void)_destroyAGLContext;
+- (void)_reshapeAGLWindow;
+- (void)_hideAGLWindow;
+- (NSImage *)_aglOffscreenImageForDrawingInRect:(NSRect)drawingInRect;
+- (void)_redeliverStream;
+@end
+
+static WebBaseNetscapePluginView *currentPluginView = nil;
+
+typedef struct OpaquePortState* PortState;
+
+#ifndef NP_NO_QUICKDRAW
+
+// QuickDraw is not available in 64-bit
+
+typedef struct {
+    GrafPtr oldPort;
+    GDHandle oldDevice;
+    Point oldOrigin;
+    RgnHandle oldClipRegion;
+    RgnHandle oldVisibleRegion;
+    RgnHandle clipRegion;
+    BOOL forUpdate;
+} PortState_QD;
+
+#endif /* NP_NO_QUICKDRAW */
+
+typedef struct {
+    CGContextRef context;
+} PortState_CG;
+
+typedef struct {
+    AGLContext oldContext;
+} PortState_GL;
+
+@interface WebPluginRequest : NSObject
+{
+    NSURLRequest *_request;
+    NSString *_frameName;
+    void *_notifyData;
+    BOOL _didStartFromUserGesture;
+    BOOL _sendNotification;
+}
+
+- (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture;
+
+- (NSURLRequest *)request;
+- (NSString *)frameName;
+- (void *)notifyData;
+- (BOOL)isCurrentEventUserGesture;
+- (BOOL)sendNotification;
+
+@end
+
+@interface NSData (WebPluginDataExtras)
+- (BOOL)_web_startsWithBlankLine;
+- (NSInteger)_web_locationAfterFirstBlankLine;
+@end
+
+static OSStatus TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *pluginView);
+
+@interface WebBaseNetscapePluginView (ForwardDeclarations)
+- (void)setWindowIfNecessary;
+- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification;
+@end
+
+@implementation WebBaseNetscapePluginView
+
++ (void)initialize
+{
+#ifndef BUILDING_ON_TIGER
+    WebCoreObjCFinalizeOnMainThread(self);
+#endif
+    WKSendUserChangeNotifications();
+}
+
+#pragma mark EVENTS
+
++ (void)getCarbonEvent:(EventRecord *)carbonEvent
+{
+    carbonEvent->what = nullEvent;
+    carbonEvent->message = 0;
+    carbonEvent->when = TickCount();
+    
+    GetGlobalMouse(&carbonEvent->where);
+    carbonEvent->where.h = static_cast<short>(carbonEvent->where.h * HIGetScaleFactor());
+    carbonEvent->where.v = static_cast<short>(carbonEvent->where.v * HIGetScaleFactor());
+    carbonEvent->modifiers = GetCurrentKeyModifiers();
+    if (!Button())
+        carbonEvent->modifiers |= btnState;
+}
+
+- (void)getCarbonEvent:(EventRecord *)carbonEvent
+{
+    [[self class] getCarbonEvent:carbonEvent];
+}
+
+- (EventModifiers)modifiersForEvent:(NSEvent *)event
+{
+    EventModifiers modifiers;
+    unsigned int modifierFlags = [event modifierFlags];
+    NSEventType eventType = [event type];
+    
+    modifiers = 0;
+    
+    if (eventType != NSLeftMouseDown && eventType != NSRightMouseDown)
+        modifiers |= btnState;
+    
+    if (modifierFlags & NSCommandKeyMask)
+        modifiers |= cmdKey;
+    
+    if (modifierFlags & NSShiftKeyMask)
+        modifiers |= shiftKey;
+
+    if (modifierFlags & NSAlphaShiftKeyMask)
+        modifiers |= alphaLock;
+
+    if (modifierFlags & NSAlternateKeyMask)
+        modifiers |= optionKey;
+
+    if (modifierFlags & NSControlKeyMask || eventType == NSRightMouseDown)
+        modifiers |= controlKey;
+    
+    return modifiers;
+}
+
+- (void)getCarbonEvent:(EventRecord *)carbonEvent withEvent:(NSEvent *)cocoaEvent
+{
+    if (WKConvertNSEventToCarbonEvent(carbonEvent, cocoaEvent)) {
+        carbonEvent->where.h = static_cast<short>(carbonEvent->where.h * HIGetScaleFactor());
+        carbonEvent->where.v = static_cast<short>(carbonEvent->where.v * HIGetScaleFactor());
+        return;
+    }
+    
+    NSPoint where = [[cocoaEvent window] convertBaseToScreen:[cocoaEvent locationInWindow]];
+        
+    carbonEvent->what = nullEvent;
+    carbonEvent->message = 0;
+    carbonEvent->when = (UInt32)([cocoaEvent timestamp] * 60); // seconds to ticks
+    carbonEvent->where.h = (short)where.x;
+    carbonEvent->where.v = (short)(NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - where.y);
+    carbonEvent->modifiers = [self modifiersForEvent:cocoaEvent];
+}
+
+- (BOOL)superviewsHaveSuperviews
+{
+    NSView *contentView = [[self window] contentView];
+    NSView *view;
+    for (view = self; view != nil; view = [view superview]) { 
+        if (view == contentView) {
+            return YES;
+        }
+    }
+    return NO;
+}
+
+#ifndef NP_NO_QUICKDRAW
+
+// The WindowRef created by -[NSWindow windowRef] has a QuickDraw GrafPort that covers 
+// the entire window frame (or structure region to use the Carbon term) rather then just the window content.
+// We can remove this when <rdar://problem/4201099> is fixed.
+- (void)fixWindowPort
+{
+    ASSERT(drawingModel == NPDrawingModelQuickDraw);
+    
+    NSWindow *currentWindow = [self currentWindow];
+    if ([currentWindow isKindOfClass:objc_getClass("NSCarbonWindow")])
+        return;
+    
+    float windowHeight = [currentWindow frame].size.height;
+    NSView *contentView = [currentWindow contentView];
+    NSRect contentRect = [contentView convertRect:[contentView frame] toView:nil]; // convert to window-relative coordinates
+    
+    CGrafPtr oldPort;
+    GetPort(&oldPort);    
+    SetPort(GetWindowPort((WindowRef)[currentWindow windowRef]));
+    
+    MovePortTo(static_cast<short>(contentRect.origin.x), /* Flip Y */ static_cast<short>(windowHeight - NSMaxY(contentRect)));
+    PortSize(static_cast<short>(contentRect.size.width), static_cast<short>(contentRect.size.height));
+    
+    SetPort(oldPort);
+}
+
+static UInt32 getQDPixelFormatForBitmapContext(CGContextRef context)
+{
+    UInt32 byteOrder = CGBitmapContextGetBitmapInfo(context) & kCGBitmapByteOrderMask;
+    if (byteOrder == kCGBitmapByteOrderDefault)
+        switch (CGBitmapContextGetBitsPerPixel(context)) {
+            case 16:
+                byteOrder = kCGBitmapByteOrder16Host;
+                break;
+            case 32:
+                byteOrder = kCGBitmapByteOrder32Host;
+                break;
+        }
+    switch (byteOrder) {
+        case kCGBitmapByteOrder16Little:
+            return k16LE555PixelFormat;
+        case kCGBitmapByteOrder32Little:
+            return k32BGRAPixelFormat;
+        case kCGBitmapByteOrder16Big:
+            return k16BE555PixelFormat;
+        case kCGBitmapByteOrder32Big:
+            return k32ARGBPixelFormat;
+    }
+    ASSERT_NOT_REACHED();
+    return 0;
+}
+
+static inline void getNPRect(const CGRect& cgr, NPRect& npr)
+{
+    npr.top = static_cast<uint16>(cgr.origin.y);
+    npr.left = static_cast<uint16>(cgr.origin.x);
+    npr.bottom = static_cast<uint16>(CGRectGetMaxY(cgr));
+    npr.right = static_cast<uint16>(CGRectGetMaxX(cgr));
+}
+
+#endif
+
+static inline void getNPRect(const NSRect& nr, NPRect& npr)
+{
+    npr.top = static_cast<uint16>(nr.origin.y);
+    npr.left = static_cast<uint16>(nr.origin.x);
+    npr.bottom = static_cast<uint16>(NSMaxY(nr));
+    npr.right = static_cast<uint16>(NSMaxX(nr));
+}
+
+- (NSRect)visibleRect
+{
+    // WebCore may impose an additional clip (via CSS overflow or clip properties).  Fetch
+    // that clip now.    
+    return NSIntersectionRect([self convertRect:[element _windowClipRect] fromView:nil], [super visibleRect]);
+}
+
+- (PortState)saveAndSetNewPortStateForUpdate:(BOOL)forUpdate
+{
+    ASSERT([self currentWindow] != nil);
+
+#ifndef NP_NO_QUICKDRAW
+    // If drawing with QuickDraw, fix the window port so that it has the same bounds as the NSWindow's
+    // content view.  This makes it easier to convert between AppKit view and QuickDraw port coordinates.
+    if (drawingModel == NPDrawingModelQuickDraw)
+        [self fixWindowPort];
+#endif
+
+    WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
+    ASSERT(windowRef);
+    
+    // Use AppKit to convert view coordinates to NSWindow coordinates.
+    NSRect boundsInWindow = [self convertRect:[self bounds] toView:nil];
+    NSRect visibleRectInWindow = [self convertRect:[self visibleRect] toView:nil];
+    
+    // Flip Y to convert NSWindow coordinates to top-left-based window coordinates.
+    float borderViewHeight = [[self currentWindow] frame].size.height;
+    boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow);
+    visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow);
+    
+#ifndef NP_NO_QUICKDRAW
+    // Look at the Carbon port to convert top-left-based window coordinates into top-left-based content coordinates.
+    if (drawingModel == NPDrawingModelQuickDraw) {
+        ::Rect portBounds;
+        CGrafPtr port = GetWindowPort(windowRef);
+        GetPortBounds(port, &portBounds);
+
+        PixMap *pix = *GetPortPixMap(port);
+        boundsInWindow.origin.x += pix->bounds.left - portBounds.left;
+        boundsInWindow.origin.y += pix->bounds.top - portBounds.top;
+        visibleRectInWindow.origin.x += pix->bounds.left - portBounds.left;
+        visibleRectInWindow.origin.y += pix->bounds.top - portBounds.top;
+    }
+#endif
+    
+    window.x = (int32)boundsInWindow.origin.x; 
+    window.y = (int32)boundsInWindow.origin.y;
+    window.width = static_cast<uint32>(NSWidth(boundsInWindow));
+    window.height = static_cast<uint32>(NSHeight(boundsInWindow));
+    
+    // "Clip-out" the plug-in when:
+    // 1) it's not really in a window or off-screen or has no height or width.
+    // 2) window.x is a "big negative number" which is how WebCore expresses off-screen widgets.
+    // 3) the window is miniaturized or the app is hidden
+    // 4) we're inside of viewWillMoveToWindow: with a nil window. In this case, superviews may already have nil 
+    // superviews and nil windows and results from convertRect:toView: are incorrect.
+    NSWindow *realWindow = [self window];
+    if (window.width <= 0 || window.height <= 0 || window.x < -100000
+            || realWindow == nil || [realWindow isMiniaturized]
+            || [NSApp isHidden]
+            || ![self superviewsHaveSuperviews]
+            || [self isHiddenOrHasHiddenAncestor]) {
+
+        // The following code tries to give plug-ins the same size they will eventually have.
+        // The specifiedWidth and specifiedHeight variables are used to predict the size that
+        // WebCore will eventually resize us to.
+
+        // The QuickTime plug-in has problems if you give it a width or height of 0.
+        // Since other plug-ins also might have the same sort of trouble, we make sure
+        // to always give plug-ins a size other than 0,0.
+
+        if (window.width <= 0)
+            window.width = specifiedWidth > 0 ? specifiedWidth : 100;
+        if (window.height <= 0)
+            window.height = specifiedHeight > 0 ? specifiedHeight : 100;
+
+        window.clipRect.bottom = window.clipRect.top;
+        window.clipRect.left = window.clipRect.right;
+    } else {
+        getNPRect(visibleRectInWindow, window.clipRect);
+    }
+    
+    // Save the port state, set up the port for entry into the plugin
+    PortState portState;
+    switch (drawingModel) {
+#ifndef NP_NO_QUICKDRAW
+        case NPDrawingModelQuickDraw: {
+            // Set up NS_Port.
+            ::Rect portBounds;
+            CGrafPtr port = GetWindowPort(windowRef);
+            GetPortBounds(port, &portBounds);
+            nPort.qdPort.port = port;
+            nPort.qdPort.portx = (int32)-boundsInWindow.origin.x;
+            nPort.qdPort.porty = (int32)-boundsInWindow.origin.y;
+            window.window = &nPort;
+
+            PortState_QD *qdPortState = (PortState_QD*)malloc(sizeof(PortState_QD));
+            portState = (PortState)qdPortState;
+            
+            GetGWorld(&qdPortState->oldPort, &qdPortState->oldDevice);    
+
+            qdPortState->oldOrigin.h = portBounds.left;
+            qdPortState->oldOrigin.v = portBounds.top;
+
+            qdPortState->oldClipRegion = NewRgn();
+            GetPortClipRegion(port, qdPortState->oldClipRegion);
+            
+            qdPortState->oldVisibleRegion = NewRgn();
+            GetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
+            
+            RgnHandle clipRegion = NewRgn();
+            qdPortState->clipRegion = clipRegion;
+
+            CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+            if (currentContext && WKCGContextIsBitmapContext(currentContext)) {
+                // We use WKCGContextIsBitmapContext here, because if we just called CGBitmapContextGetData
+                // on any context, we'd log to the console every time. But even if WKCGContextIsBitmapContext
+                // returns true, it still might not be a context we need to create a GWorld for; for example
+                // transparency layers will return true, but return 0 for CGBitmapContextGetData.
+                void* offscreenData = CGBitmapContextGetData(currentContext);
+                if (offscreenData) {
+                    // If the current context is an offscreen bitmap, then create a GWorld for it.
+                    ::Rect offscreenBounds;
+                    offscreenBounds.top = 0;
+                    offscreenBounds.left = 0;
+                    offscreenBounds.right = CGBitmapContextGetWidth(currentContext);
+                    offscreenBounds.bottom = CGBitmapContextGetHeight(currentContext);
+                    GWorldPtr newOffscreenGWorld;
+                    QDErr err = NewGWorldFromPtr(&newOffscreenGWorld,
+                        getQDPixelFormatForBitmapContext(currentContext), &offscreenBounds, 0, 0, 0,
+                        static_cast<char*>(offscreenData), CGBitmapContextGetBytesPerRow(currentContext));
+                    ASSERT(newOffscreenGWorld && !err);
+                    if (!err) {
+                        if (offscreenGWorld)
+                            DisposeGWorld(offscreenGWorld);
+                        offscreenGWorld = newOffscreenGWorld;
+
+                        SetGWorld(offscreenGWorld, NULL);
+
+                        port = offscreenGWorld;
+
+                        nPort.qdPort.port = port;
+                        boundsInWindow = [self bounds];
+                        
+                        // Generate a QD origin based on the current affine transform for currentContext.
+                        CGAffineTransform offscreenMatrix = CGContextGetCTM(currentContext);
+                        CGPoint origin = {0,0};
+                        CGPoint axisFlip = {1,1};
+                        origin = CGPointApplyAffineTransform(origin, offscreenMatrix);
+                        axisFlip = CGPointApplyAffineTransform(axisFlip, offscreenMatrix);
+                        
+                        // Quartz bitmaps have origins at the bottom left, but the axes may be inverted, so handle that.
+                        origin.x = offscreenBounds.left - origin.x * (axisFlip.x - origin.x);
+                        origin.y = offscreenBounds.bottom + origin.y * (axisFlip.y - origin.y);
+                        
+                        nPort.qdPort.portx = static_cast<int32>(-boundsInWindow.origin.x + origin.x);
+                        nPort.qdPort.porty = static_cast<int32>(-boundsInWindow.origin.y - origin.y);
+                        window.x = 0;
+                        window.y = 0;
+                        window.window = &nPort;
+
+                        // Use the clip bounds from the context instead of the bounds we created
+                        // from the window above.
+                        getNPRect(CGContextGetClipBoundingBox(currentContext), window.clipRect);
+                    }
+                }
+            }
+
+            MacSetRectRgn(clipRegion,
+                window.clipRect.left + nPort.qdPort.portx, window.clipRect.top + nPort.qdPort.porty,
+                window.clipRect.right + nPort.qdPort.portx, window.clipRect.bottom + nPort.qdPort.porty);
+            
+            // Clip to dirty region so plug-in does not draw over already-drawn regions of the window that are
+            // not going to be redrawn this update.  This forces plug-ins to play nice with z-index ordering.
+            if (forUpdate) {
+                RgnHandle viewClipRegion = NewRgn();
+                
+                // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
+                // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
+                // knows about the true set of dirty rects.
+                NSView *opaqueAncestor = [self opaqueAncestor];
+                const NSRect *dirtyRects;
+                NSInteger dirtyRectCount, dirtyRectIndex;
+                [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount];
+
+                for (dirtyRectIndex = 0; dirtyRectIndex < dirtyRectCount; dirtyRectIndex++) {
+                    NSRect dirtyRect = [self convertRect:dirtyRects[dirtyRectIndex] fromView:opaqueAncestor];
+                    if (!NSEqualSizes(dirtyRect.size, NSZeroSize)) {
+                        // Create a region for this dirty rect
+                        RgnHandle dirtyRectRegion = NewRgn();
+                        SetRectRgn(dirtyRectRegion, static_cast<short>(NSMinX(dirtyRect)), static_cast<short>(NSMinY(dirtyRect)), static_cast<short>(NSMaxX(dirtyRect)), static_cast<short>(NSMaxY(dirtyRect)));
+                        
+                        // Union this dirty rect with the rest of the dirty rects
+                        UnionRgn(viewClipRegion, dirtyRectRegion, viewClipRegion);
+                        DisposeRgn(dirtyRectRegion);
+                    }
+                }
+            
+                // Intersect the dirty region with the clip region, so that we only draw over dirty parts
+                SectRgn(clipRegion, viewClipRegion, clipRegion);
+                DisposeRgn(viewClipRegion);
+            }
+
+            // Switch to the port and set it up.
+            SetPort(port);
+            PenNormal();
+            ForeColor(blackColor);
+            BackColor(whiteColor);
+            SetOrigin(nPort.qdPort.portx, nPort.qdPort.porty);
+            SetPortClipRegion(nPort.qdPort.port, clipRegion);
+
+            if (forUpdate) {
+                // AppKit may have tried to help us by doing a BeginUpdate.
+                // But the invalid region at that level didn't include AppKit's notion of what was not valid.
+                // We reset the port's visible region to counteract what BeginUpdate did.
+                SetPortVisibleRegion(nPort.qdPort.port, clipRegion);
+                InvalWindowRgn(windowRef, clipRegion);
+            }
+            
+            qdPortState->forUpdate = forUpdate;
+            break;
+        }
+#endif /* NP_NO_QUICKDRAW */
+
+        case NPDrawingModelCoreGraphics: {            
+            // A CoreGraphics plugin's window may only be set while the plugin view is being updated
+            ASSERT(forUpdate && [NSView focusView] == self);
+
+            CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
+
+            PortState_CG *cgPortState = (PortState_CG *)malloc(sizeof(PortState_CG));
+            portState = (PortState)cgPortState;
+            cgPortState->context = context;
+            
+            // Update the plugin's window/context
+            nPort.cgPort.window = windowRef;
+            nPort.cgPort.context = context;
+            window.window = &nPort.cgPort;
+
+            // Save current graphics context's state; will be restored by -restorePortState:
+            CGContextSaveGState(context);
+            
+            // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
+            // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
+            // knows about the true set of dirty rects.
+            NSView *opaqueAncestor = [self opaqueAncestor];
+            const NSRect *dirtyRects;
+            NSInteger count;
+            [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&count];
+            Vector<CGRect, 16> convertedDirtyRects;
+            convertedDirtyRects.resize(count);
+            for (int i = 0; i < count; ++i)
+                reinterpret_cast<NSRect&>(convertedDirtyRects[i]) = [self convertRect:dirtyRects[i] fromView:opaqueAncestor];
+            CGContextClipToRects(context, convertedDirtyRects.data(), count);
+
+            break;
+        }
+
+        case NPDrawingModelOpenGL: {
+            // An OpenGL plugin's window may only be set while the plugin view is being updated
+            ASSERT(forUpdate && [NSView focusView] == self);
+
+            // Clear the "current" window and context -- they will be assigned below (if all goes well)
+            nPort.aglPort.window = NULL;
+            nPort.aglPort.context = NULL;
+            
+            // Create AGL context if needed
+            if (![self _createAGLContextIfNeeded]) {
+                LOG_ERROR("Could not create AGL context");
+                return NULL;
+            }
+            
+            // Update the plugin's window/context
+            nPort.aglPort.window = windowRef;
+            nPort.aglPort.context = [self _cglContext];
+            window.window = &nPort.aglPort;
+            
+            // Save/set current AGL context
+            PortState_GL *glPortState = (PortState_GL *)malloc(sizeof(PortState_GL));
+            portState = (PortState)glPortState;
+            glPortState->oldContext = aglGetCurrentContext();
+            aglSetCurrentContext(aglContext);
+            
+            // Adjust viewport according to clip
+            switch (window.type) {
+                case NPWindowTypeWindow:
+                    glViewport(static_cast<GLint>(NSMinX(boundsInWindow) - NSMinX(visibleRectInWindow)),
+                        static_cast<GLint>(NSMaxY(visibleRectInWindow) - NSMaxY(boundsInWindow)),
+                            window.width, window.height);
+                    break;
+                
+                case NPWindowTypeDrawable: {
+                    GLsizei width, height;
+                    if ([self _getAGLOffscreenBuffer:NULL width:&width height:&height])
+                        glViewport(0, 0, width, height);
+                    break;
+                }
+                
+                default:
+                    ASSERT_NOT_REACHED();
+                    break;
+            }
+            break;
+        }
+        
+        default:
+            ASSERT_NOT_REACHED();
+            portState = NULL;
+            break;
+    }
+    
+    return portState;
+}
+
+- (PortState)saveAndSetNewPortState
+{
+    return [self saveAndSetNewPortStateForUpdate:NO];
+}
+
+- (void)restorePortState:(PortState)portState
+{
+    ASSERT([self currentWindow]);
+    ASSERT(portState);
+    
+    switch (drawingModel) {
+#ifndef NP_NO_QUICKDRAW
+        case NPDrawingModelQuickDraw: {
+            PortState_QD *qdPortState = (PortState_QD *)portState;
+            WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
+            CGrafPtr port = GetWindowPort(windowRef);
+
+            SetPort(port);
+
+            if (qdPortState->forUpdate)
+                ValidWindowRgn(windowRef, qdPortState->clipRegion);
+
+            SetOrigin(qdPortState->oldOrigin.h, qdPortState->oldOrigin.v);
+
+            SetPortClipRegion(port, qdPortState->oldClipRegion);
+            if (qdPortState->forUpdate)
+                SetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
+
+            DisposeRgn(qdPortState->oldClipRegion);
+            DisposeRgn(qdPortState->oldVisibleRegion);
+            DisposeRgn(qdPortState->clipRegion);
+
+            SetGWorld(qdPortState->oldPort, qdPortState->oldDevice);
+            break;
+        }
+#endif /* NP_NO_QUICKDRAW */
+        
+        case NPDrawingModelCoreGraphics:
+            ASSERT([NSView focusView] == self);
+            ASSERT(((PortState_CG *)portState)->context == nPort.cgPort.context);
+            CGContextRestoreGState(nPort.cgPort.context);
+            break;
+        
+        case NPDrawingModelOpenGL:
+            aglSetCurrentContext(((PortState_GL *)portState)->oldContext);
+            break;
+        
+        default:
+            ASSERT_NOT_REACHED();
+            break;
+    }
+}
+
+- (BOOL)sendEvent:(EventRecord *)event
+{
+    if (![self window])
+        return NO;
+    ASSERT(event);
+   
+    // If at any point the user clicks or presses a key from within a plugin, set the 
+    // currentEventIsUserGesture flag to true. This is important to differentiate legitimate 
+    // window.open() calls;  we still want to allow those.  See rdar://problem/4010765
+    if (event->what == mouseDown || event->what == keyDown || event->what == mouseUp || event->what == autoKey)
+        currentEventIsUserGesture = YES;
+    
+    suspendKeyUpEvents = NO;
+    
+    if (!isStarted)
+        return NO;
+
+    ASSERT(NPP_HandleEvent);
+    
+    // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
+    // We probably don't want more general reentrancy protection; we are really
+    // protecting only against this one case, which actually comes up when
+    // you first install the SVG viewer plug-in.
+    if (inSetWindow)
+        return NO;
+
+    Frame* frame = core([self webFrame]);
+    if (!frame)
+        return NO;
+    Page* page = frame->page();
+    if (!page)
+        return NO;
+
+    bool wasDeferring = page->defersLoading();
+    if (!wasDeferring)
+        page->setDefersLoading(true);
+
+    // Can only send updateEvt to CoreGraphics and OpenGL plugins when actually drawing
+    ASSERT((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || event->what != updateEvt || [NSView focusView] == self);
+    
+    BOOL updating = event->what == updateEvt;
+    PortState portState;
+    if ((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || event->what == updateEvt) {
+        // 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
+        // allowed to draw at any other time.
+        portState = [self saveAndSetNewPortStateForUpdate:updating];
+        
+        // We may have changed the window, so inform the plug-in.
+        [self setWindowIfNecessary];
+    } else
+        portState = NULL;
+    
+#if !defined(NDEBUG) && !defined(NP_NO_QUICKDRAW)
+    // Draw green to help debug.
+    // If we see any green we know something's wrong.
+    // Note that PaintRect() only works for QuickDraw plugins; otherwise the current QD port is undefined.
+    if (drawingModel == NPDrawingModelQuickDraw && !isTransparent && event->what == updateEvt) {
+        ForeColor(greenColor);
+        const ::Rect bigRect = { -10000, -10000, 10000, 10000 };
+        PaintRect(&bigRect);
+        ForeColor(blackColor);
+    }
+#endif
+    
+    // Temporarily retain self in case the plug-in view is released while sending an event. 
+    [[self retain] autorelease];
+    
+    BOOL acceptedEvent;
+    [self willCallPlugInFunction];
+    {
+        KJS::JSLock::DropAllLocks dropAllLocks;
+        acceptedEvent = NPP_HandleEvent(plugin, event);
+    }
+    [self didCallPlugInFunction];
+    
+    currentEventIsUserGesture = NO;
+    
+    if (portState) {
+        if ([self currentWindow])
+            [self restorePortState:portState];
+        free(portState);
+    }
+
+    if (!wasDeferring)
+        page->setDefersLoading(false);
+            
+    return acceptedEvent;
+}
+
+- (void)sendActivateEvent:(BOOL)activate
+{
+    EventRecord event;
+    
+    [self getCarbonEvent:&event];
+    event.what = activateEvt;
+    WindowRef windowRef = (WindowRef)[[self window] windowRef];
+    event.message = (unsigned long)windowRef;
+    if (activate) {
+        event.modifiers |= activeFlag;
+    }
+    
+    BOOL acceptedEvent;
+    acceptedEvent = [self sendEvent:&event]; 
+    
+    LOG(PluginEvents, "NPP_HandleEvent(activateEvent): %d  isActive: %d", acceptedEvent, activate);
+}
+
+- (BOOL)sendUpdateEvent
+{
+    EventRecord event;
+    
+    [self getCarbonEvent:&event];
+    event.what = updateEvt;
+    WindowRef windowRef = (WindowRef)[[self window] windowRef];
+    event.message = (unsigned long)windowRef;
+
+    BOOL acceptedEvent = [self sendEvent:&event];
+
+    LOG(PluginEvents, "NPP_HandleEvent(updateEvt): %d", acceptedEvent);
+
+    return acceptedEvent;
+}
+
+-(void)sendNullEvent
+{
+    EventRecord event;
+
+    [self getCarbonEvent:&event];
+
+    // Plug-in should not react to cursor position when not active or when a menu is down.
+    MenuTrackingData trackingData;
+    OSStatus error = GetMenuTrackingData(NULL, &trackingData);
+
+    // Plug-in should not react to cursor position when the actual window is not key.
+    if (![[self window] isKeyWindow] || (error == noErr && trackingData.menu)) {
+        // FIXME: Does passing a v and h of -1 really prevent it from reacting to the cursor position?
+        event.where.v = -1;
+        event.where.h = -1;
+    }
+    
+    [self sendEvent:&event];
+}
+
+- (void)stopNullEvents
+{
+    [nullEventTimer invalidate];
+    [nullEventTimer release];
+    nullEventTimer = nil;
+}
+
+- (void)restartNullEvents
+{
+    ASSERT([self window]);
+    
+    if (nullEventTimer)
+        [self stopNullEvents];
+    
+    if (!isStarted || [[self window] isMiniaturized])
+        return;
+
+    NSTimeInterval interval;
+
+    // If the plugin is completely obscured (scrolled out of view, for example), then we will
+    // send null events at a reduced rate.
+    interval = !isCompletelyObscured ? NullEventIntervalActive : NullEventIntervalNotActive;    
+    nullEventTimer = [[NSTimer scheduledTimerWithTimeInterval:interval
+                                                       target:self
+                                                     selector:@selector(sendNullEvent)
+                                                     userInfo:nil
+                                                      repeats:YES] retain];
+}
+
+- (BOOL)acceptsFirstResponder
+{
+    return YES;
+}
+
+- (void)installKeyEventHandler
+{
+    static const EventTypeSpec sTSMEvents[] =
+    {
+    { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
+    };
+    
+    if (!keyEventHandler) {
+        InstallEventHandler(GetWindowEventTarget((WindowRef)[[self window] windowRef]),
+                            NewEventHandlerUPP(TSMEventHandler),
+                            GetEventTypeCount(sTSMEvents),
+                            sTSMEvents,
+                            self,
+                            &keyEventHandler);
+    }
+}
+
+- (void)removeKeyEventHandler
+{
+    if (keyEventHandler) {
+        RemoveEventHandler(keyEventHandler);
+        keyEventHandler = NULL;
+    }
+}
+
+- (void)setHasFocus:(BOOL)flag
+{
+    if (hasFocus != flag) {
+        hasFocus = flag;
+        EventRecord event;
+        [self getCarbonEvent:&event];
+        BOOL acceptedEvent;
+        if (hasFocus) {
+            event.what = getFocusEvent;
+            acceptedEvent = [self sendEvent:&event]; 
+            LOG(PluginEvents, "NPP_HandleEvent(getFocusEvent): %d", acceptedEvent);
+            [self installKeyEventHandler];
+        } else {
+            event.what = loseFocusEvent;
+            acceptedEvent = [self sendEvent:&event]; 
+            LOG(PluginEvents, "NPP_HandleEvent(loseFocusEvent): %d", acceptedEvent);
+            [self removeKeyEventHandler];
+        }
+    }
+}
+
+- (BOOL)becomeFirstResponder
+{
+    [self setHasFocus:YES];
+    return YES;
+}
+
+- (BOOL)resignFirstResponder
+{
+    [self setHasFocus:NO];    
+    return YES;
+}
+
+// AppKit doesn't call mouseDown or mouseUp on right-click. Simulate control-click
+// mouseDown and mouseUp so plug-ins get the right-click event as they do in Carbon (3125743).
+- (void)rightMouseDown:(NSEvent *)theEvent
+{
+    [self mouseDown:theEvent];
+}
+
+- (void)rightMouseUp:(NSEvent *)theEvent
+{
+    [self mouseUp:theEvent];
+}
+
+- (void)mouseDown:(NSEvent *)theEvent
+{
+    EventRecord event;
+
+    [self getCarbonEvent:&event withEvent:theEvent];
+    event.what = mouseDown;
+
+    BOOL acceptedEvent;
+    acceptedEvent = [self sendEvent:&event]; 
+    
+    LOG(PluginEvents, "NPP_HandleEvent(mouseDown): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
+}
+
+- (void)mouseUp:(NSEvent *)theEvent
+{
+    EventRecord event;
+    
+    [self getCarbonEvent:&event withEvent:theEvent];
+    event.what = mouseUp;
+
+    BOOL acceptedEvent;
+    acceptedEvent = [self sendEvent:&event]; 
+    
+    LOG(PluginEvents, "NPP_HandleEvent(mouseUp): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
+}
+
+- (void)mouseEntered:(NSEvent *)theEvent
+{
+    EventRecord event;
+    
+    [self getCarbonEvent:&event withEvent:theEvent];
+    event.what = adjustCursorEvent;
+
+    BOOL acceptedEvent;
+    acceptedEvent = [self sendEvent:&event]; 
+
+    LOG(PluginEvents, "NPP_HandleEvent(mouseEntered): %d", acceptedEvent);
+}
+
+- (void)mouseExited:(NSEvent *)theEvent
+{
+    EventRecord event;
+    
+    [self getCarbonEvent:&event withEvent:theEvent];
+    event.what = adjustCursorEvent;
+
+    BOOL acceptedEvent;
+    acceptedEvent = [self sendEvent:&event]; 
+
+    // Set cursor back to arrow cursor.  Because NSCursor doesn't know about changes that the plugin made, we could get confused about what we think the
+    // current cursor is otherwise.  Therefore we have no choice but to unconditionally reset the cursor when the mouse exits the plugin.
+    [[NSCursor arrowCursor] set];
+
+    LOG(PluginEvents, "NPP_HandleEvent(mouseExited): %d", acceptedEvent);
+}
+
+- (void)mouseDragged:(NSEvent *)theEvent
+{
+    // Do nothing so that other responders don't respond to the drag that initiated in this view.
+}
+
+- (UInt32)keyMessageForEvent:(NSEvent *)event
+{
+    NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())];
+    if (!data) {
+        return 0;
+    }
+    UInt8 characterCode;
+    [data getBytes:&characterCode length:1];
+    UInt16 keyCode = [event keyCode];
+    return keyCode << 8 | characterCode;
+}
+
+- (void)keyUp:(NSEvent *)theEvent
+{
+    WKSendKeyEventToTSM(theEvent);
+    
+    // TSM won't send keyUp events so we have to send them ourselves.
+    // Only send keyUp events after we receive the TSM callback because this is what plug-in expect from OS 9.
+    if (!suspendKeyUpEvents) {
+        EventRecord event;
+        
+        [self getCarbonEvent:&event withEvent:theEvent];
+        event.what = keyUp;
+        
+        if (event.message == 0) {
+            event.message = [self keyMessageForEvent:theEvent];
+        }
+        
+        [self sendEvent:&event];
+    }
+}
+
+- (void)keyDown:(NSEvent *)theEvent
+{
+    suspendKeyUpEvents = YES;
+    WKSendKeyEventToTSM(theEvent);
+}
+
+static OSStatus TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *pluginView)
+{    
+    EventRef rawKeyEventRef;
+    OSStatus status = GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(EventRef), NULL, &rawKeyEventRef);
+    if (status != noErr) {
+        LOG_ERROR("GetEventParameter failed with error: %d", status);
+        return noErr;
+    }
+    
+    // Two-pass read to allocate/extract Mac charCodes
+    ByteCount numBytes;    
+    status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL);
+    if (status != noErr) {
+        LOG_ERROR("GetEventParameter failed with error: %d", status);
+        return noErr;
+    }
+    char *buffer = (char *)malloc(numBytes);
+    status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, numBytes, NULL, buffer);
+    if (status != noErr) {
+        LOG_ERROR("GetEventParameter failed with error: %d", status);
+        free(buffer);
+        return noErr;
+    }
+    
+    EventRef cloneEvent = CopyEvent(rawKeyEventRef);
+    unsigned i;
+    for (i = 0; i < numBytes; i++) {
+        status = SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes, typeChar, 1 /* one char code */, &buffer[i]);
+        if (status != noErr) {
+            LOG_ERROR("SetEventParameter failed with error: %d", status);
+            free(buffer);
+            return noErr;
+        }
+        
+        EventRecord eventRec;
+        if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
+            BOOL acceptedEvent;
+            acceptedEvent = [(WebBaseNetscapePluginView *)pluginView sendEvent:&eventRec];
+            
+            LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu",
+                acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask));
+            
+            // We originally thought that if the plug-in didn't accept this event,
+            // we should pass it along so that keyboard scrolling, for example, will work.
+            // In practice, this is not a good idea, because plug-ins tend to eat the event but return false.
+            // MacIE handles each key event twice because of this, but we will emulate the other browsers instead.
+        }
+    }
+    ReleaseEvent(cloneEvent);
+    
+    free(buffer);
+
+    return noErr;
+}
+
+// Fake up command-modified events so cut, copy, paste and select all menus work.
+- (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character
+{
+    EventRecord event;
+    [self getCarbonEvent:&event];
+    event.what = keyDown;
+    event.modifiers |= cmdKey;
+    event.message = keyCode << 8 | character;
+    [self sendEvent:&event];
+}
+
+- (void)cut:(id)sender
+{
+    [self sendModifierEventWithKeyCode:7 character:'x'];
+}
+
+- (void)copy:(id)sender
+{
+    [self sendModifierEventWithKeyCode:8 character:'c'];
+}
+
+- (void)paste:(id)sender
+{
+    [self sendModifierEventWithKeyCode:9 character:'v'];
+}
+
+- (void)selectAll:(id)sender
+{
+    [self sendModifierEventWithKeyCode:0 character:'a'];
+}
+
+#pragma mark WEB_NETSCAPE_PLUGIN
+
+- (BOOL)isNewWindowEqualToOldWindow
+{
+    if (window.x != lastSetWindow.x)
+        return NO;
+    if (window.y != lastSetWindow.y)
+        return NO;
+    if (window.width != lastSetWindow.width)
+        return NO;
+    if (window.height != lastSetWindow.height)
+        return NO;
+    if (window.clipRect.top != lastSetWindow.clipRect.top)
+        return NO;
+    if (window.clipRect.left != lastSetWindow.clipRect.left)
+        return NO;
+    if (window.clipRect.bottom  != lastSetWindow.clipRect.bottom)
+        return NO;
+    if (window.clipRect.right != lastSetWindow.clipRect.right)
+        return NO;
+    if (window.type != lastSetWindow.type)
+        return NO;
+    
+    switch (drawingModel) {
+#ifndef NP_NO_QUICKDRAW
+        case NPDrawingModelQuickDraw:
+            if (nPort.qdPort.portx != lastSetPort.qdPort.portx)
+                return NO;
+            if (nPort.qdPort.porty != lastSetPort.qdPort.porty)
+                return NO;
+            if (nPort.qdPort.port != lastSetPort.qdPort.port)
+                return NO;
+        break;
+#endif /* NP_NO_QUICKDRAW */
+            
+        case NPDrawingModelCoreGraphics:
+            if (nPort.cgPort.window != lastSetPort.cgPort.window)
+                return NO;
+            if (nPort.cgPort.context != lastSetPort.cgPort.context)
+                return NO;
+        break;
+            
+        case NPDrawingModelOpenGL:
+            if (nPort.aglPort.window != lastSetPort.aglPort.window)
+                return NO;
+            if (nPort.aglPort.context != lastSetPort.aglPort.context)
+                return NO;
+        break;
+        
+        default:
+            ASSERT_NOT_REACHED();
+        break;
+    }
+    
+    return YES;
+}
+
+- (void)updateAndSetWindow
+{
+    if (drawingModel == NPDrawingModelCoreGraphics || drawingModel == NPDrawingModelOpenGL) {
+        // Can only update CoreGraphics and OpenGL plugins while redrawing the plugin view
+        [self setNeedsDisplay:YES];
+        return;
+    }
+    
+    // Can't update the plugin if it has not started (or has been stopped)
+    if (!isStarted)
+        return;
+        
+    PortState portState = [self saveAndSetNewPortState];
+    if (portState) {
+        [self setWindowIfNecessary];
+        [self restorePortState:portState];
+        free(portState);
+    }
+}
+
+- (void)setWindowIfNecessary
+{
+    if (!isStarted) {
+        return;
+    }
+    
+    if (![self isNewWindowEqualToOldWindow]) {        
+        // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
+        // We probably don't want more general reentrancy protection; we are really
+        // protecting only against this one case, which actually comes up when
+        // you first install the SVG viewer plug-in.
+        NPError npErr;
+        ASSERT(!inSetWindow);
+        
+        inSetWindow = YES;
+        
+        // A CoreGraphics or OpenGL plugin's window may only be set while the plugin is being updated
+        ASSERT((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || [NSView focusView] == self);
+        
+        [self willCallPlugInFunction];
+        {
+            KJS::JSLock::DropAllLocks dropAllLocks;
+            npErr = NPP_SetWindow(plugin, &window);
+        }
+        [self didCallPlugInFunction];
+        inSetWindow = NO;
+
+#ifndef NDEBUG
+        switch (drawingModel) {
+#ifndef NP_NO_QUICKDRAW
+            case NPDrawingModelQuickDraw:
+                LOG(Plugins, "NPP_SetWindow (QuickDraw): %d, port=0x%08x, window.x:%d window.y:%d window.width:%d window.height:%d",
+                npErr, (int)nPort.qdPort.port, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
+            break;
+#endif /* NP_NO_QUICKDRAW */
+            
+            case NPDrawingModelCoreGraphics:
+                LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d",
+                npErr, nPort.cgPort.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
+            break;
+
+            case NPDrawingModelOpenGL:
+                LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d",
+                npErr, nPort.aglPort.window, nPort.aglPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
+            break;
+            
+            default:
+                ASSERT_NOT_REACHED();
+            break;
+        }
+#endif /* !defined(NDEBUG) */
+        
+        lastSetWindow = window;
+        lastSetPort = nPort;
+    }
+}
+
+- (void)removeTrackingRect
+{
+    if (trackingTag) {
+        [self removeTrackingRect:trackingTag];
+        trackingTag = 0;
+
+        // Do the following after setting trackingTag to 0 so we don't re-enter.
+
+        // Balance the retain in resetTrackingRect. Use autorelease in case we hold 
+        // the last reference to the window during tear-down, to avoid crashing AppKit. 
+        [[self window] autorelease];
+    }
+}
+
+- (void)resetTrackingRect
+{
+    [self removeTrackingRect];
+    if (isStarted) {
+        // Retain the window so that removeTrackingRect can work after the window is closed.
+        [[self window] retain];
+        trackingTag = [self addTrackingRect:[self bounds] owner:self userData:nil assumeInside:NO];
+    }
+}
+
++ (void)setCurrentPluginView:(WebBaseNetscapePluginView *)view
+{
+    currentPluginView = view;
+}
+
++ (WebBaseNetscapePluginView *)currentPluginView
+{
+    return currentPluginView;
+}
+
+- (BOOL)canStart
+{
+    return YES;
+}
+
+- (void)didStart
+{
+    if (_loadManually) {
+        [self _redeliverStream];
+        return;
+    }
+    
+    // If the OBJECT/EMBED tag has no SRC, the URL is passed to us as "".
+    // Check for this and don't start a load in this case.
+    if (sourceURL != nil && ![sourceURL _web_isEmpty]) {
+        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:sourceURL];
+        [request _web_setHTTPReferrer:core([self webFrame])->loader()->outgoingReferrer()];
+        [self loadRequest:request inTarget:nil withNotifyData:nil sendNotification:NO];
+    } 
+}
+
+- (void)addWindowObservers
+{
+    ASSERT([self window]);
+
+    NSWindow *theWindow = [self window];
+    
+    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
+    [notificationCenter addObserver:self selector:@selector(windowWillClose:) 
+                               name:NSWindowWillCloseNotification object:theWindow]; 
+    [notificationCenter addObserver:self selector:@selector(windowBecameKey:)
+                               name:NSWindowDidBecomeKeyNotification object:theWindow];
+    [notificationCenter addObserver:self selector:@selector(windowResignedKey:)
+                               name:NSWindowDidResignKeyNotification object:theWindow];
+    [notificationCenter addObserver:self selector:@selector(windowDidMiniaturize:)
+                               name:NSWindowDidMiniaturizeNotification object:theWindow];
+    [notificationCenter addObserver:self selector:@selector(windowDidDeminiaturize:)
+                               name:NSWindowDidDeminiaturizeNotification object:theWindow];
+    
+    [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchFromUser:)
+                               name:LoginWindowDidSwitchFromUserNotification object:nil];
+    [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchToUser:)
+                               name:LoginWindowDidSwitchToUserNotification object:nil];
+}
+
+- (void)removeWindowObservers
+{
+    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
+    [notificationCenter removeObserver:self name:NSWindowWillCloseNotification        object:nil]; 
+    [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification     object:nil];
+    [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification     object:nil];
+    [notificationCenter removeObserver:self name:NSWindowDidMiniaturizeNotification   object:nil];
+    [notificationCenter removeObserver:self name:NSWindowDidDeminiaturizeNotification object:nil];
+    [notificationCenter removeObserver:self name:LoginWindowDidSwitchFromUserNotification   object:nil];
+    [notificationCenter removeObserver:self name:LoginWindowDidSwitchToUserNotification     object:nil];
+}
+
+- (BOOL)start
+{
+    ASSERT([self currentWindow]);
+    
+    if (isStarted)
+        return YES;
+
+    if (![self canStart])
+        return NO;
+    
+    ASSERT([self webView]);
+    
+    if (![[[self webView] preferences] arePlugInsEnabled])
+        return NO;
+
+    // Open the plug-in package so it remains loaded while our plugin uses it
+    [pluginPackage open];
+    
+    // Initialize drawingModel to an invalid value so that we can detect when the plugin does not specify a drawingModel
+    drawingModel = (NPDrawingModel)-1;
+    
+    // Plug-ins are "windowed" by default.  On MacOS, windowed plug-ins share the same window and graphics port as the main
+    // browser window.  Windowless plug-ins are rendered off-screen, then copied into the main browser window.
+    window.type = NPWindowTypeWindow;
+    
+    NPError npErr = [self _createPlugin];
+    if (npErr != NPERR_NO_ERROR) {
+        LOG_ERROR("NPP_New failed with error: %d", npErr);
+        [self _destroyPlugin];
+        [pluginPackage close];
+        return NO;
+    }
+    
+    if (drawingModel == (NPDrawingModel)-1) {
+#ifndef NP_NO_QUICKDRAW
+        // Default to QuickDraw if the plugin did not specify a drawing model.
+        drawingModel = NPDrawingModelQuickDraw;
+#else
+        // QuickDraw is not available, so we can't default to it.  We could default to CoreGraphics instead, but
+        // if the plugin did not specify the CoreGraphics drawing model then it must be one of the old QuickDraw
+        // plugins.  Thus, the plugin is unsupported and should not be started.  Destroy it here and bail out.
+        LOG(Plugins, "Plugin only supports QuickDraw, but QuickDraw is unavailable: %@", pluginPackage);
+        [self _destroyPlugin];
+        [pluginPackage close];
+        return NO;
+#endif
+    }
+
+    isStarted = YES;
+        
+    [self updateAndSetWindow];
+
+    if ([self window]) {
+        [self addWindowObservers];
+        if ([[self window] isKeyWindow]) {
+            [self sendActivateEvent:YES];
+        }
+        [self restartNullEvents];
+    }
+
+    [self resetTrackingRect];
+    
+    [self didStart];
+    
+    return YES;
+}
+
+- (void)stop
+{
+    // If we're already calling a plug-in function, do not call NPP_Destroy().  The plug-in function we are calling
+    // may assume that its instance->pdata, or other memory freed by NPP_Destroy(), is valid and unchanged until said
+    // plugin-function returns.
+    // See <rdar://problem/4480737>.
+    if (pluginFunctionCallDepth > 0) {
+        shouldStopSoon = YES;
+        return;
+    }
+    
+    [self removeTrackingRect];
+
+    if (!isStarted)
+        return;
+    
+    isStarted = NO;
+    // To stop active streams it's necessary to invoke makeObjectsPerformSelector on a copy 
+    // of streams. This is because calling -[WebNetscapePluginStream stop] also has the side effect
+    // of removing a stream from this collection.
+    NSArray *streamsCopy = [streams copy];
+    [streamsCopy makeObjectsPerformSelector:@selector(stop)];
+    [streamsCopy release];
+   
+    // Stop the null events
+    [self stopNullEvents];
+    
+    // Stop notifications and callbacks.
+    [self removeWindowObservers];
+    [[pendingFrameLoads allKeys] makeObjectsPerformSelector:@selector(_setInternalLoadDelegate:) withObject:nil];
+    [NSObject cancelPreviousPerformRequestsWithTarget:self];
+
+    // Setting the window type to 0 ensures that NPP_SetWindow will be called if the plug-in is restarted.
+    lastSetWindow.type = (NPWindowType)0;
+    
+    [self _destroyPlugin];
+    [pluginPackage close];
+    
+    // We usually remove the key event handler in resignFirstResponder but it is possible that resignFirstResponder 
+    // may never get called so we can't completely rely on it.
+    [self removeKeyEventHandler];
+    
+    if (drawingModel == NPDrawingModelOpenGL)
+        [self _destroyAGLContext];
+}
+
+- (BOOL)isStarted
+{
+    return isStarted;
+}
+
+- (WebDataSource *)dataSource
+{
+    WebFrame *webFrame = kit(core(element)->document()->frame());
+    return [webFrame _dataSource];
+}
+
+- (WebFrame *)webFrame
+{
+    return [[self dataSource] webFrame];
+}
+
+- (WebView *)webView
+{
+    return [[self webFrame] webView];
+}
+
+- (NSWindow *)currentWindow
+{
+    return [self window] ? [self window] : [[self webView] hostWindow];
+}
+
+- (NPP)plugin
+{
+    return plugin;
+}
+
+- (WebNetscapePluginPackage *)pluginPackage
+{
+    return pluginPackage;
+}
+
+- (void)setPluginPackage:(WebNetscapePluginPackage *)thePluginPackage;
+{
+    [thePluginPackage retain];
+    [pluginPackage release];
+    pluginPackage = thePluginPackage;
+
+    NPP_New =           [pluginPackage NPP_New];
+    NPP_Destroy =       [pluginPackage NPP_Destroy];
+    NPP_SetWindow =     [pluginPackage NPP_SetWindow];
+    NPP_NewStream =     [pluginPackage NPP_NewStream];
+    NPP_WriteReady =    [pluginPackage NPP_WriteReady];
+    NPP_Write =         [pluginPackage NPP_Write];
+    NPP_StreamAsFile =  [pluginPackage NPP_StreamAsFile];
+    NPP_DestroyStream = [pluginPackage NPP_DestroyStream];
+    NPP_HandleEvent =   [pluginPackage NPP_HandleEvent];
+    NPP_URLNotify =     [pluginPackage NPP_URLNotify];
+    NPP_GetValue =      [pluginPackage NPP_GetValue];
+    NPP_SetValue =      [pluginPackage NPP_SetValue];
+    NPP_Print =         [pluginPackage NPP_Print];
+}
+
+- (void)setMIMEType:(NSString *)theMIMEType
+{
+    NSString *type = [theMIMEType copy];
+    [MIMEType release];
+    MIMEType = type;
+}
+
+- (void)setBaseURL:(NSURL *)theBaseURL
+{
+    [theBaseURL retain];
+    [baseURL release];
+    baseURL = theBaseURL;
+}
+
+- (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values;
+{
+    ASSERT([keys count] == [values count]);
+    
+    // Convert the attributes to 2 C string arrays.
+    // These arrays are passed to NPP_New, but the strings need to be
+    // modifiable and live the entire life of the plugin.
+
+    // The Java plug-in requires the first argument to be the base URL
+    if ([MIMEType isEqualToString:@"application/x-java-applet"]) {
+        cAttributes = (char **)malloc(([keys count] + 1) * sizeof(char *));
+        cValues = (char **)malloc(([values count] + 1) * sizeof(char *));
+        cAttributes[0] = strdup("DOCBASE");
+        cValues[0] = strdup([baseURL _web_URLCString]);
+        argsCount++;
+    } else {
+        cAttributes = (char **)malloc([keys count] * sizeof(char *));
+        cValues = (char **)malloc([values count] * sizeof(char *));
+    }
+
+    BOOL isWMP = [[[pluginPackage bundle] bundleIdentifier] isEqualToString:@"com.microsoft.WMP.defaultplugin"];
+    
+    unsigned i;
+    unsigned count = [keys count];
+    for (i = 0; i < count; i++) {
+        NSString *key = [keys objectAtIndex:i];
+        NSString *value = [values objectAtIndex:i];
+        if ([key _webkit_isCaseInsensitiveEqualToString:@"height"]) {
+            specifiedHeight = [value intValue];
+        } else if ([key _webkit_isCaseInsensitiveEqualToString:@"width"]) {
+            specifiedWidth = [value intValue];
+        }
+        // Avoid Window Media Player crash when these attributes are present.
+        if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) {
+            continue;
+        }
+        cAttributes[argsCount] = strdup([key UTF8String]);
+        cValues[argsCount] = strdup([value UTF8String]);
+        LOG(Plugins, "%@ = %@", key, value);
+        argsCount++;
+    }
+}
+
+- (void)setMode:(int)theMode
+{
+    mode = theMode;
+}
+
+#pragma mark NSVIEW
+
+- (id)initWithFrame:(NSRect)frame
+      pluginPackage:(WebNetscapePluginPackage *)thePluginPackage
+                URL:(NSURL *)theURL
+            baseURL:(NSURL *)theBaseURL
+           MIMEType:(NSString *)MIME
+      attributeKeys:(NSArray *)keys
+    attributeValues:(NSArray *)values
+       loadManually:(BOOL)loadManually
+         DOMElement:(DOMElement *)anElement
+{
+    [super initWithFrame:frame];
+ 
+    streams = [[NSMutableArray alloc] init];
+    pendingFrameLoads = [[NSMutableDictionary alloc] init];    
+    
+    // load the plug-in if it is not already loaded
+    if (![thePluginPackage load]) {
+        [self release];
+        return nil;
+    }
+    [self setPluginPackage:thePluginPackage];
+    
+    element = [anElement retain];
+    sourceURL = [theURL retain];
+    
+    [self setMIMEType:MIME];
+    [self setBaseURL:theBaseURL];
+    [self setAttributeKeys:keys andValues:values];
+    if (loadManually)
+        [self setMode:NP_FULL];
+    else
+        [self setMode:NP_EMBED];
+    
+    _loadManually = loadManually;
+    
+    return self;
+}
+
+- (id)initWithFrame:(NSRect)frame
+{
+    ASSERT_NOT_REACHED();
+    return nil;
+}
+
+- (void)fini
+{
+#ifndef NP_NO_QUICKDRAW
+    if (offscreenGWorld)
+        DisposeGWorld(offscreenGWorld);
+#endif
+
+    unsigned i;
+    for (i = 0; i < argsCount; i++) {
+        free(cAttributes[i]);
+        free(cValues[i]);
+    }
+    free(cAttributes);
+    free(cValues);
+}
+
+- (void)disconnectStream:(WebBaseNetscapePluginStream*)stream
+{
+    [streams removeObjectIdenticalTo:stream];    
+}
+
+- (void)dealloc
+{
+    ASSERT(!isStarted);
+
+    [sourceURL release];
+    [_manualStream release];
+    [_error release];
+    
+    [pluginPackage release];
+    [streams release];
+    [MIMEType release];
+    [baseURL release];
+    [pendingFrameLoads release];
+    [element release];
+    
+    ASSERT(!plugin);
+    ASSERT(!aglWindow);
+    ASSERT(!aglContext);
+
+    [self fini];
+
+    [super dealloc];
+}
+
+- (void)finalize
+{
+    ASSERT_MAIN_THREAD();
+    ASSERT(!isStarted);
+
+    [self fini];
+
+    [super finalize];
+}
+
+- (void)drawRect:(NSRect)rect
+{
+    if (!isStarted) {
+        return;
+    }
+    
+    if ([NSGraphicsContext currentContextDrawingToScreen])
+        [self sendUpdateEvent];
+    else {
+        NSBitmapImageRep *printedPluginBitmap = [self _printedPluginBitmap];
+        if (printedPluginBitmap) {
+            // Flip the bitmap before drawing because the QuickDraw port is flipped relative
+            // to this view.
+            CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+            CGContextSaveGState(cgContext);
+            NSRect bounds = [self bounds];
+            CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
+            CGContextScaleCTM(cgContext, 1.0f, -1.0f);
+            [printedPluginBitmap drawInRect:bounds];
+            CGContextRestoreGState(cgContext);
+        }
+    }
+    
+    // If this is a windowless OpenGL plugin, blit its contents back into this view.  The plug-in just drew into the offscreen context.
+    if (drawingModel == NPDrawingModelOpenGL && window.type == NPWindowTypeDrawable) {
+        NSImage *aglOffscreenImage = [self _aglOffscreenImageForDrawingInRect:rect];
+        if (aglOffscreenImage) {
+            // Flip the context before drawing because the CGL context is flipped relative to this view.
+            CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+            CGContextSaveGState(cgContext);
+            NSRect bounds = [self bounds];
+            CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
+            CGContextScaleCTM(cgContext, 1.0f, -1.0f);
+            
+            // Copy 'rect' from the offscreen buffer to this view (the flip above makes this sort of tricky)
+            NSRect flippedRect = rect;
+            flippedRect.origin.y = NSMaxY(bounds) - NSMaxY(flippedRect);
+            [aglOffscreenImage drawInRect:flippedRect fromRect:flippedRect operation:NSCompositeSourceOver fraction:1.0f];
+            CGContextRestoreGState(cgContext);
+        }
+    }
+}
+
+- (BOOL)isFlipped
+{
+    return YES;
+}
+
+- (void)renewGState
+{
+    [super renewGState];
+    
+    // -renewGState is called whenever the view's geometry changes.  It's a little hacky to override this method, but
+    // much safer than walking up the view hierarchy and observing frame/bounds changed notifications, since you don't
+    // have to track subsequent changes to the view hierarchy and add/remove notification observers.
+    // NSOpenGLView uses the exact same technique to reshape its OpenGL surface.
+    [self _viewHasMoved];
+}
+
+#ifndef NP_NO_QUICKDRAW
+-(void)tellQuickTimeToChill
+{
+    ASSERT(drawingModel == NPDrawingModelQuickDraw);
+    
+    // Make a call to the secret QuickDraw API that makes QuickTime calm down.
+    WindowRef windowRef = (WindowRef)[[self window] windowRef];
+    if (!windowRef) {
+        return;
+    }
+    CGrafPtr port = GetWindowPort(windowRef);
+    ::Rect bounds;
+    GetPortBounds(port, &bounds);
+    WKCallDrawingNotification(port, &bounds);
+}
+#endif /* NP_NO_QUICKDRAW */
+
+- (void)viewWillMoveToWindow:(NSWindow *)newWindow
+{
+#ifndef NP_NO_QUICKDRAW
+    if (drawingModel == NPDrawingModelQuickDraw)
+        [self tellQuickTimeToChill];
+#endif
+
+    // We must remove the tracking rect before we move to the new window.
+    // Once we move to the new window, it will be too late.
+    [self removeTrackingRect];
+    [self removeWindowObservers];
+    
+    // Workaround for: <rdar://problem/3822871> resignFirstResponder is not sent to first responder view when it is removed from the window
+    [self setHasFocus:NO];
+
+    if (!newWindow) {
+        // Hide the AGL child window
+        if (drawingModel == NPDrawingModelOpenGL)
+            [self _hideAGLWindow];
+        
+        if ([[self webView] hostWindow]) {
+            // View will be moved out of the actual window but it still has a host window.
+            [self stopNullEvents];
+        } else {
+            // View will have no associated windows.
+            [self stop];
+
+            // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
+            // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
+            [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
+        }
+    }
+}
+
+- (void)viewWillMoveToSuperview:(NSView *)newSuperview
+{
+    if (!newSuperview) {
+        // Stop the plug-in when it is removed from its superview.  It is not sufficient to do this in -viewWillMoveToWindow:nil, because
+        // the WebView might still has a hostWindow at that point, which prevents the plug-in from being destroyed.
+        // There is no need to start the plug-in when moving into a superview.  -viewDidMoveToWindow takes care of that.
+        [self stop];
+        
+        // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
+        // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
+        [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
+    }
+}
+
+- (void)viewDidMoveToWindow
+{
+    [self resetTrackingRect];
+    
+    if ([self window]) {
+        // While in the view hierarchy, observe WebPreferencesChangedNotification so that we can start/stop depending
+        // on whether plugins are enabled.
+        [[NSNotificationCenter defaultCenter] addObserver:self
+                                              selector:@selector(preferencesHaveChanged:)
+                                              name:WebPreferencesChangedNotification
+                                              object:nil];
+
+        // View moved to an actual window. Start it if not already started.
+        [self start];
+        [self restartNullEvents];
+        [self addWindowObservers];
+    } else if ([[self webView] hostWindow]) {
+        // View moved out of an actual window, but still has a host window.
+        // Call setWindow to explicitly "clip out" the plug-in from sight.
+        // FIXME: It would be nice to do this where we call stopNullEvents in viewWillMoveToWindow.
+        [self updateAndSetWindow];
+    }
+}
+
+- (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
+{
+    if (!hostWindow && ![self window]) {
+        // View will have no associated windows.
+        [self stop];
+
+        // Remove WebPreferencesChangedNotification observer -- we will observe once again when we move back into the window
+        [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
+    }
+}
+
+- (void)viewDidMoveToHostWindow
+{
+    if ([[self webView] hostWindow]) {
+        // View now has an associated window. Start it if not already started.
+        [self start];
+    }
+}
+
+#pragma mark NOTIFICATIONS
+
+- (void)windowWillClose:(NSNotification *)notification 
+{
+    [self stop]; 
+} 
+
+- (void)windowBecameKey:(NSNotification *)notification
+{
+    [self sendActivateEvent:YES];
+    [self setNeedsDisplay:YES];
+    [self restartNullEvents];
+    SetUserFocusWindow((WindowRef)[[self window] windowRef]);
+}
+
+- (void)windowResignedKey:(NSNotification *)notification
+{
+    [self sendActivateEvent:NO];
+    [self setNeedsDisplay:YES];
+    [self restartNullEvents];
+}
+
+- (void)windowDidMiniaturize:(NSNotification *)notification
+{
+    [self stopNullEvents];
+}
+
+- (void)windowDidDeminiaturize:(NSNotification *)notification
+{
+    [self restartNullEvents];
+}
+
+- (void)loginWindowDidSwitchFromUser:(NSNotification *)notification
+{
+    [self stopNullEvents];
+}
+
+-(void)loginWindowDidSwitchToUser:(NSNotification *)notification
+{
+    [self restartNullEvents];
+}
+
+- (void)preferencesHaveChanged:(NSNotification *)notification
+{
+    WebPreferences *preferences = [[self webView] preferences];
+    BOOL arePlugInsEnabled = [preferences arePlugInsEnabled];
+    
+    if ([notification object] == preferences && isStarted != arePlugInsEnabled) {
+        if (arePlugInsEnabled) {
+            if ([self currentWindow]) {
+                [self start];
+            }
+        } else {
+            [self stop];
+            [self setNeedsDisplay:YES];
+        }
+    }
+}
+
+- (NPObject *)createPluginScriptableObject
+{
+    if (!NPP_GetValue || ![self isStarted])
+        return NULL;
+        
+    NPObject *value = NULL;
+    NPError error;
+    [self willCallPlugInFunction];
+    {
+        KJS::JSLock::DropAllLocks dropAllLocks;
+        error = NPP_GetValue(plugin, NPPVpluginScriptableNPObject, &value);
+    }
+    [self didCallPlugInFunction];
+    if (error != NPERR_NO_ERROR)
+        return NULL;
+    
+    return value;
+}
+
+- (void)willCallPlugInFunction
+{
+    ASSERT(plugin);
+
+    // Could try to prevent infinite recursion here, but it's probably not worth the effort.
+    pluginFunctionCallDepth++;
+}
+
+- (void)didCallPlugInFunction
+{
+    ASSERT(pluginFunctionCallDepth > 0);
+    pluginFunctionCallDepth--;
+    
+    // If -stop was called while we were calling into a plug-in function, and we're no longer
+    // inside a plug-in function, stop now.
+    if (pluginFunctionCallDepth == 0 && shouldStopSoon) {
+        shouldStopSoon = NO;
+        [self stop];
+    }
+}
+
+-(void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
+{
+    ASSERT(_loadManually);
+    ASSERT(!_manualStream);
+    
+    _manualStream = [[WebNetscapePluginStream alloc] initWithFrameLoader:core([self webFrame])->loader()];
+}
+
+- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
+{
+    ASSERT(_loadManually);
+    ASSERT(_manualStream);
+    
+    _dataLengthReceived += [data length];
+    
+    if (![self isStarted])
+        return;
+    
+    if ([_manualStream plugin] == NULL) {
+        [_manualStream setRequestURL:[[[self dataSource] request] URL]];
+        [_manualStream setPlugin:[self plugin]];
+        ASSERT([_manualStream plugin]);
+        [_manualStream startStreamWithResponse:[[self dataSource] response]];
+    }
+    
+    if ([_manualStream plugin])
+        [_manualStream receivedData:data];
+}
+
+- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
+{
+    ASSERT(_loadManually);
+    
+    [error retain];
+    [_error release];
+    _error = error;
+    
+    if (![self isStarted]) {
+        return;
+    }
+    
+    [_manualStream destroyStreamWithError:error];
+}
+
+- (void)pluginViewFinishedLoading:(NSView *)pluginView 
+{
+    ASSERT(_loadManually);
+    ASSERT(_manualStream);
+    
+    if ([self isStarted])
+        [_manualStream finishedLoadingWithData:[[self dataSource] data]];    
+}
+
+@end
+
+@implementation WebBaseNetscapePluginView (WebNPPCallbacks)
+
+- (NSMutableURLRequest *)requestWithURLCString:(const char *)URLCString
+{
+    if (!URLCString)
+        return nil;
+    
+    CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, URLCString, kCFStringEncodingISOLatin1);
+    ASSERT(string); // All strings should be representable in ISO Latin 1
+    
+    NSString *URLString = [(NSString *)string _web_stringByStrippingReturnCharacters];
+    NSURL *URL = [NSURL _web_URLWithDataAsString:URLString relativeToURL:baseURL];
+    CFRelease(string);
+    if (!URL)
+        return nil;
+
+    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
+    Frame* frame = core([self webFrame]);
+    if (!frame)
+        return nil;
+    [request _web_setHTTPReferrer:frame->loader()->outgoingReferrer()];
+    return request;
+}
+
+- (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest
+{
+    // FIXME: Is this isStarted check needed here? evaluateJavaScriptPluginRequest should not be called
+    // if we are stopped since this method is called after a delay and we call 
+    // cancelPreviousPerformRequestsWithTarget inside of stop.
+    if (!isStarted) {
+        return;
+    }
+    
+    NSURL *URL = [[JSPluginRequest request] URL];
+    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
+    ASSERT(JSString);
+    
+    NSString *result = [[[self webFrame] _bridge] stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]];
+    
+    // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop.
+    if (!isStarted) {
+        return;
+    }
+        
+    if ([JSPluginRequest frameName] != nil) {
+        // FIXME: If the result is a string, we probably want to put that string into the frame.
+        if ([JSPluginRequest sendNotification]) {
+            [self willCallPlugInFunction];
+            {
+                KJS::JSLock::DropAllLocks dropAllLocks;
+                NPP_URLNotify(plugin, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]);
+            }
+            [self didCallPlugInFunction];
+        }
+    } else if ([result length] > 0) {
+        // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does.
+        NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding];
+        WebBaseNetscapePluginStream *stream = [[WebBaseNetscapePluginStream alloc] initWithRequestURL:URL
+                                                                                               plugin:plugin
+                                                                                           notifyData:[JSPluginRequest notifyData]
+                                                                                     sendNotification:[JSPluginRequest sendNotification]];
+        [stream startStreamResponseURL:URL
+                 expectedContentLength:[JSData length]
+                      lastModifiedDate:nil
+                              MIMEType:@"text/plain"
+                               headers:nil];
+        [stream receivedData:JSData];
+        [stream finishedLoadingWithData:JSData];
+        [stream release];
+    }
+}
+
+- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
+{
+    ASSERT(isStarted);
+    
+    WebPluginRequest *pluginRequest = [pendingFrameLoads objectForKey:webFrame];
+    ASSERT(pluginRequest != nil);
+    ASSERT([pluginRequest sendNotification]);
+        
+    [self willCallPlugInFunction];
+    {
+        KJS::JSLock::DropAllLocks dropAllLocks;
+        NPP_URLNotify(plugin, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]);
+    }
+    [self didCallPlugInFunction];
+    
+    [pendingFrameLoads removeObjectForKey:webFrame];
+    [webFrame _setInternalLoadDelegate:nil];
+}
+
+- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
+{
+    NPReason reason = NPRES_DONE;
+    if (error != nil) {
+        reason = [WebBaseNetscapePluginStream reasonForError:error];
+    }    
+    [self webFrame:webFrame didFinishLoadWithReason:reason];
+}
+
+- (void)loadPluginRequest:(WebPluginRequest *)pluginRequest
+{
+    NSURLRequest *request = [pluginRequest request];
+    NSString *frameName = [pluginRequest frameName];
+    WebFrame *frame = nil;
+    
+    NSURL *URL = [request URL];
+    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
+    
+    ASSERT(frameName || JSString);
+    
+    if (frameName) {
+        // FIXME - need to get rid of this window creation which
+        // bypasses normal targeted link handling
+        frame = [[self webFrame] findFrameNamed:frameName];
+    
+        if (frame == nil) {
+            WebView *currentWebView = [self webView];
+            WebView *newWebView = CallUIDelegate(currentWebView, @selector(webView:createWebViewWithRequest:), nil);
+
+            if (!newWebView) {
+                if ([pluginRequest sendNotification]) {
+                    [self willCallPlugInFunction];
+                    {
+                        KJS::JSLock::DropAllLocks dropAllLocks;
+                        NPP_URLNotify(plugin, [[[pluginRequest request] URL] _web_URLCString], NPERR_GENERIC_ERROR, [pluginRequest notifyData]);
+                    }
+                    [self didCallPlugInFunction];
+                }
+                return;
+            }
+            
+            frame = [newWebView mainFrame];
+            core(frame)->tree()->setName(frameName);
+            [[newWebView _UIDelegateForwarder] webViewShow:newWebView];
+        }
+    }
+
+    if (JSString) {
+        ASSERT(frame == nil || [self webFrame] == frame);
+        [self evaluateJavaScriptPluginRequest:pluginRequest];
+    } else {
+        [frame loadRequest:request];
+        if ([pluginRequest sendNotification]) {
+            // Check if another plug-in view or even this view is waiting for the frame to load.
+            // If it is, tell it that the load was cancelled because it will be anyway.
+            WebBaseNetscapePluginView *view = [frame _internalLoadDelegate];
+            if (view != nil) {
+                ASSERT([view isKindOfClass:[WebBaseNetscapePluginView class]]);
+                [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK];
+            }
+            [pendingFrameLoads _webkit_setObject:pluginRequest forUncopiedKey:frame];
+            [frame _setInternalLoadDelegate:self];
+        }
+    }
+}
+
+- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification
+{
+    NSURL *URL = [request URL];
+
+    if (!URL) 
+        return NPERR_INVALID_URL;
+
+    NSString *target = nil;
+    if (cTarget) {
+        // Find the frame given the target string.
+        target = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, cTarget, kCFStringEncodingWindowsLatin1);
+    }
+    WebFrame *frame = [self webFrame];
+
+    // don't let a plugin start any loads if it is no longer part of a document that is being 
+    // displayed unless the loads are in the same frame as the plugin.
+    if ([[self dataSource] _documentLoader] != [[self webFrame] _frameLoader]->activeDocumentLoader() &&
+        (!cTarget || [frame findFrameNamed:target] != frame)) {
+        if (target)
+            CFRelease(target);
+        return NPERR_GENERIC_ERROR; 
+    }
+    
+    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
+    if (JSString != nil) {
+        if (![[[self webView] preferences] isJavaScriptEnabled]) {
+            // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does.
+            return NPERR_GENERIC_ERROR;
+        } else if (cTarget == NULL && mode == NP_FULL) {
+            // Don't allow a JavaScript request from a standalone plug-in that is self-targetted
+            // because this can cause the user to be redirected to a blank page (3424039).
+            return NPERR_INVALID_PARAM;
+        }
+    }
+        
+    if (cTarget || JSString) {
+        // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't
+        // want to potentially kill the plug-in inside of its URL request.
+        
+        if (JSString != nil && target != nil && [frame findFrameNamed:target] != frame) {
+            // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
+            CFRelease(target);
+            return NPERR_INVALID_PARAM;
+        }
+        
+        WebPluginRequest *pluginRequest = [[WebPluginRequest alloc] initWithRequest:request frameName:target notifyData:notifyData sendNotification:sendNotification didStartFromUserGesture:currentEventIsUserGesture];
+        [self performSelector:@selector(loadPluginRequest:) withObject:pluginRequest afterDelay:0];
+        [pluginRequest release];
+        if (target)
+            CFRelease(target);
+    } else {
+        WebNetscapePluginStream *stream = [[WebNetscapePluginStream alloc] initWithRequest:request 
+                                                                                    plugin:plugin 
+                                                                                notifyData:notifyData 
+                                                                          sendNotification:sendNotification];
+        if (!stream)
+            return NPERR_INVALID_URL;
+
+        [streams addObject:stream];
+        [stream start];
+        [stream release];
+    }
+    
+    return NPERR_NO_ERROR;
+}
+
+-(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData
+{
+    LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget);
+
+    NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
+    return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES];
+}
+
+-(NPError)getURL:(const char *)URLCString target:(const char *)cTarget
+{
+    LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget);
+
+    NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
+    return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO];
+}
+
+- (NPError)_postURL:(const char *)URLCString
+             target:(const char *)target
+                len:(UInt32)len
+                buf:(const char *)buf
+               file:(NPBool)file
+         notifyData:(void *)notifyData
+   sendNotification:(BOOL)sendNotification
+       allowHeaders:(BOOL)allowHeaders
+{
+    if (!URLCString || !len || !buf) {
+        return NPERR_INVALID_PARAM;
+    }
+    
+    NSData *postData = nil;
+
+    if (file) {
+        // If we're posting a file, buf is either a file URL or a path to the file.
+        NSString *bufString = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingWindowsLatin1);
+        if (!bufString) {
+            return NPERR_INVALID_PARAM;
+        }
+        NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString];
+        NSString *path;
+        if ([fileURL isFileURL]) {
+            path = [fileURL path];
+        } else {
+            path = bufString;
+        }
+        postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]];
+        CFRelease(bufString);
+        if (!postData) {
+            return NPERR_FILE_NOT_FOUND;
+        }
+    } else {
+        postData = [NSData dataWithBytes:buf length:len];
+    }
+
+    if ([postData length] == 0) {
+        return NPERR_INVALID_PARAM;
+    }
+
+    NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
+    [request setHTTPMethod:@"POST"];
+    
+    if (allowHeaders) {
+        if ([postData _web_startsWithBlankLine]) {
+            postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)];
+        } else {
+            NSInteger location = [postData _web_locationAfterFirstBlankLine];
+            if (location != NSNotFound) {
+                // If the blank line is somewhere in the middle of postData, everything before is the header.
+                NSData *headerData = [postData subdataWithRange:NSMakeRange(0, location)];
+                NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields];
+                unsigned dataLength = [postData length] - location;
+
+                // Sometimes plugins like to set Content-Length themselves when they post,
+                // but WebFoundation does not like that. So we will remove the header
+                // and instead truncate the data to the requested length.
+                NSString *contentLength = [header objectForKey:@"Content-Length"];
+
+                if (contentLength != nil)
+                    dataLength = MIN((unsigned)[contentLength intValue], dataLength);
+                [header removeObjectForKey:@"Content-Length"];
+
+                if ([header count] > 0) {
+                    [request setAllHTTPHeaderFields:header];
+                }
+                // Everything after the blank line is the actual content of the POST.
+                postData = [postData subdataWithRange:NSMakeRange(location, dataLength)];
+
+            }
+        }
+        if ([postData length] == 0) {
+            return NPERR_INVALID_PARAM;
+        }
+    }
+
+    // Plug-ins expect to receive uncached data when doing a POST (3347134).
+    [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
+    [request setHTTPBody:postData];
+    
+    return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification];
+}
+
+- (NPError)postURLNotify:(const char *)URLCString
+                  target:(const char *)target
+                     len:(UInt32)len
+                     buf:(const char *)buf
+                    file:(NPBool)file
+              notifyData:(void *)notifyData
+{
+    LOG(Plugins, "NPN_PostURLNotify: %s", URLCString);
+    return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:notifyData sendNotification:YES allowHeaders:YES];
+}
+
+-(NPError)postURL:(const char *)URLCString
+           target:(const char *)target
+              len:(UInt32)len
+              buf:(const char *)buf
+             file:(NPBool)file
+{
+    LOG(Plugins, "NPN_PostURL: %s", URLCString);        
+    // As documented, only allow headers to be specified via NPP_PostURL when using a file.
+    return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:NULL sendNotification:NO allowHeaders:file];
+}
+
+-(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream
+{
+    LOG(Plugins, "NPN_NewStream");
+    return NPERR_GENERIC_ERROR;
+}
+
+-(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer
+{
+    LOG(Plugins, "NPN_Write");
+    return NPERR_GENERIC_ERROR;
+}
+
+-(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason
+{
+    LOG(Plugins, "NPN_DestroyStream");
+    // This function does a sanity check to ensure that the NPStream provided actually
+    // belongs to the plug-in that provided it, which fixes a crash in the DivX 
+    // plug-in: <rdar://problem/5093862> | http://bugs.webkit.org/show_bug.cgi?id=13203
+    if (!stream || [WebBaseNetscapePluginStream ownerForStream:stream] != plugin) {
+        LOG(Plugins, "Invalid NPStream passed to NPN_DestroyStream: %p", stream);
+        return NPERR_INVALID_INSTANCE_ERROR;
+    }
+    
+    WebBaseNetscapePluginStream *browserStream = static_cast<WebBaseNetscapePluginStream *>(stream->ndata);
+    [browserStream cancelLoadAndDestroyStreamWithError:[browserStream errorForReason:reason]];
+    
+    return NPERR_NO_ERROR;
+}
+
+- (const char *)userAgent
+{
+    return [[[self webView] userAgentForURL:baseURL] UTF8String];
+}
+
+-(void)status:(const char *)message
+{    
+    if (!message) {
+        LOG_ERROR("NPN_Status passed a NULL status message");
+        return;
+    }
+
+    CFStringRef status = CFStringCreateWithCString(NULL, message, kCFStringEncodingUTF8);
+    if (!status) {
+        LOG_ERROR("NPN_Status: the message was not valid UTF-8");
+        return;
+    }
+    
+    LOG(Plugins, "NPN_Status: %@", status);
+    WebView *wv = [self webView];
+    [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status];
+    CFRelease(status);
+}
+
+-(void)invalidateRect:(NPRect *)invalidRect
+{
+    LOG(Plugins, "NPN_InvalidateRect");
+    [self setNeedsDisplayInRect:NSMakeRect(invalidRect->left, invalidRect->top,
+        (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)];
+}
+
+-(bool)isOpaque
+{
+    return YES;
+}
+
+- (void)invalidateRegion:(NPRegion)invalidRegion
+{
+    LOG(Plugins, "NPN_InvalidateRegion");
+    NSRect invalidRect = NSZeroRect;
+    switch (drawingModel) {
+#ifndef NP_NO_QUICKDRAW
+        case NPDrawingModelQuickDraw:
+        {
+            ::Rect qdRect;
+            GetRegionBounds((NPQDRegion)invalidRegion, &qdRect);
+            invalidRect = NSMakeRect(qdRect.left, qdRect.top, qdRect.right - qdRect.left, qdRect.bottom - qdRect.top);
+        }
+        break;
+#endif /* NP_NO_QUICKDRAW */
+        
+        case NPDrawingModelCoreGraphics:
+        case NPDrawingModelOpenGL:
+        {
+            CGRect cgRect = CGPathGetBoundingBox((NPCGRegion)invalidRegion);
+            invalidRect = *(NSRect *)&cgRect;
+        }
+        break;
+    
+        default:
+            ASSERT_NOT_REACHED();
+        break;
+    }
+    
+    [self setNeedsDisplayInRect:invalidRect];
+}
+
+-(void)forceRedraw
+{
+    LOG(Plugins, "forceRedraw");
+    [self setNeedsDisplay:YES];
+    [[self window] displayIfNeeded];
+}
+
+- (NPError)getVariable:(NPNVariable)variable value:(void *)value
+{
+    switch (variable) {
+        case NPNVWindowNPObject:
+        {
+            Frame* frame = core([self webFrame]);
+            NPObject* windowScriptObject = frame ? frame->windowScriptNPObject() : 0;
+
+            // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
+            if (windowScriptObject)
+                _NPN_RetainObject(windowScriptObject);
+            
+            void **v = (void **)value;
+            *v = windowScriptObject;
+
+            return NPERR_NO_ERROR;
+        }
+
+        case NPNVPluginElementNPObject:
+        {
+            if (!element)
+                return NPERR_GENERIC_ERROR;
+            
+            NPObject *plugInScriptObject = (NPObject *)[element _NPObject];
+
+            // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
+            if (plugInScriptObject)
+                _NPN_RetainObject(plugInScriptObject);
+
+            void **v = (void **)value;
+            *v = plugInScriptObject;
+
+            return NPERR_NO_ERROR;
+        }
+        
+        case NPNVpluginDrawingModel:
+        {
+            *(NPDrawingModel *)value = drawingModel;
+            return NPERR_NO_ERROR;
+        }
+
+#ifndef NP_NO_QUICKDRAW
+        case NPNVsupportsQuickDrawBool:
+        {
+            *(NPBool *)value = TRUE;
+            return NPERR_NO_ERROR;
+        }
+#endif /* NP_NO_QUICKDRAW */
+        
+        case NPNVsupportsCoreGraphicsBool:
+        {
+            *(NPBool *)value = TRUE;
+            return NPERR_NO_ERROR;
+        }
+
+        case NPNVsupportsOpenGLBool:
+        {
+            *(NPBool *)value = TRUE;
+            return NPERR_NO_ERROR;
+        }
+        
+        default:
+            break;
+    }
+
+    return NPERR_GENERIC_ERROR;
+}
+
+- (NPError)setVariable:(NPPVariable)variable value:(void *)value
+{
+    switch (variable) {
+        case NPPVpluginWindowBool:
+        {
+            NPWindowType newWindowType = (value ? NPWindowTypeWindow : NPWindowTypeDrawable);
+
+            // Redisplay if window type is changing (some drawing models can only have their windows set while updating).
+            if (newWindowType != window.type)
+                [self setNeedsDisplay:YES];
+            
+            window.type = newWindowType;
+        }
+        
+        case NPPVpluginTransparentBool:
+        {
+            BOOL newTransparent = (value != 0);
+            
+            // Redisplay if transparency is changing
+            if (isTransparent != newTransparent)
+                [self setNeedsDisplay:YES];
+            
+            isTransparent = newTransparent;
+            
+            return NPERR_NO_ERROR;
+        }
+        
+        case NPNVpluginDrawingModel:
+        {
+            // Can only set drawing model inside NPP_New()
+            if (self != [[self class] currentPluginView])
+                return NPERR_GENERIC_ERROR;
+            
+            // Check for valid, supported drawing model
+            NPDrawingModel newDrawingModel = (NPDrawingModel)(uintptr_t)value;
+            switch (newDrawingModel) {
+                // Supported drawing models:
+#ifndef NP_NO_QUICKDRAW
+                case NPDrawingModelQuickDraw:
+#endif
+                case NPDrawingModelCoreGraphics:
+                case NPDrawingModelOpenGL:
+                    drawingModel = newDrawingModel;
+                    return NPERR_NO_ERROR;
+                
+                // Unsupported (or unknown) drawing models:
+                default:
+                    LOG(Plugins, "Plugin %@ uses unsupported drawing model: %d", pluginPackage, drawingModel);
+                    return NPERR_GENERIC_ERROR;
+            }
+        }
+        
+        default:
+            return NPERR_GENERIC_ERROR;
+    }
+}
+
+@end
+
+@implementation WebPluginRequest
+
+- (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture
+{
+    [super init];
+    _didStartFromUserGesture = currentEventIsUserGesture;
+    _request = [request retain];
+    _frameName = [frameName retain];
+    _notifyData = notifyData;
+    _sendNotification = sendNotification;
+    return self;
+}
+
+- (void)dealloc
+{
+    [_request release];
+    [_frameName release];
+    [super dealloc];
+}
+
+- (NSURLRequest *)request
+{
+    return _request;
+}
+
+- (NSString *)frameName
+{
+    return _frameName;
+}
+
+- (BOOL)isCurrentEventUserGesture
+{
+    return _didStartFromUserGesture;
+}
+
+- (BOOL)sendNotification
+{
+    return _sendNotification;
+}
+
+- (void *)notifyData
+{
+    return _notifyData;
+}
+
+@end
+
+@implementation WebBaseNetscapePluginView (Internal)
+
+- (NPError)_createPlugin
+{
+    plugin = (NPP)calloc(1, sizeof(NPP_t));
+    plugin->ndata = self;
+
+    ASSERT(NPP_New);
+
+    // NPN_New(), which creates the plug-in instance, should never be called while calling a plug-in function for that instance.
+    ASSERT(pluginFunctionCallDepth == 0);
+
+    [[self class] setCurrentPluginView:self];
+    NPError npErr = NPP_New((char *)[MIMEType cString], plugin, mode, argsCount, cAttributes, cValues, NULL);
+    [[self class] setCurrentPluginView:nil];
+    
+    LOG(Plugins, "NPP_New: %d", npErr);
+    return npErr;
+}
+
+- (void)_destroyPlugin
+{
+    NPError npErr;
+    npErr = NPP_Destroy(plugin, NULL);
+    LOG(Plugins, "NPP_Destroy: %d", npErr);
+    
+    if (Frame* frame = core([self webFrame]))
+        frame->cleanupScriptObjectsForPlugin(self);
+        
+    free(plugin);
+    plugin = NULL;
+}
+
+- (void)_viewHasMoved
+{
+    // All of the work this method does may safely be skipped if the view is not in a window.  When the view
+    // is moved back into a window, everything should be set up correctly.
+    if (![self window])
+        return;
+    
+    if (drawingModel == NPDrawingModelOpenGL)
+        [self _reshapeAGLWindow];
+
+#ifndef NP_NO_QUICKDRAW
+    if (drawingModel == NPDrawingModelQuickDraw)
+        [self tellQuickTimeToChill];
+#endif
+    [self updateAndSetWindow];
+    [self resetTrackingRect];
+    
+    // Check to see if the plugin view is completely obscured (scrolled out of view, for example).
+    // For performance reasons, we send null events at a lower rate to plugins which are obscured.
+    BOOL oldIsObscured = isCompletelyObscured;
+    isCompletelyObscured = NSIsEmptyRect([self visibleRect]);
+    if (isCompletelyObscured != oldIsObscured)
+        [self restartNullEvents];
+}
+
+- (NSBitmapImageRep *)_printedPluginBitmap
+{
+#ifdef NP_NO_QUICKDRAW
+    return nil;
+#else
+    // Cannot print plugins that do not implement NPP_Print
+    if (!NPP_Print)
+        return nil;
+
+    // This NSBitmapImageRep will share its bitmap buffer with a GWorld that the plugin will draw into.
+    // The bitmap is created in 32-bits-per-pixel ARGB format, which is the default GWorld pixel format.
+    NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
+                                                         pixelsWide:window.width
+                                                         pixelsHigh:window.height
+                                                         bitsPerSample:8
+                                                         samplesPerPixel:4
+                                                         hasAlpha:YES
+                                                         isPlanar:NO
+                                                         colorSpaceName:NSDeviceRGBColorSpace
+                                                         bitmapFormat:NSAlphaFirstBitmapFormat
+                                                         bytesPerRow:0
+                                                         bitsPerPixel:0] autorelease];
+    ASSERT(bitmap);
+    
+    // Create a GWorld with the same underlying buffer into which the plugin can draw
+    ::Rect printGWorldBounds;
+    SetRect(&printGWorldBounds, 0, 0, window.width, window.height);
+    GWorldPtr printGWorld;
+    if (NewGWorldFromPtr(&printGWorld,
+                         k32ARGBPixelFormat,
+                         &printGWorldBounds,
+                         NULL,
+                         NULL,
+                         0,
+                         (Ptr)[bitmap bitmapData],
+                         [bitmap bytesPerRow]) != noErr) {
+        LOG_ERROR("Could not create GWorld for printing");
+        return nil;
+    }
+    
+    /// Create NPWindow for the GWorld
+    NPWindow printNPWindow;
+    printNPWindow.window = &printGWorld; // Normally this is an NP_Port, but when printing it is the actual CGrafPtr
+    printNPWindow.x = 0;
+    printNPWindow.y = 0;
+    printNPWindow.width = window.width;
+    printNPWindow.height = window.height;
+    printNPWindow.clipRect.top = 0;
+    printNPWindow.clipRect.left = 0;
+    printNPWindow.clipRect.right = window.width;
+    printNPWindow.clipRect.bottom = window.height;
+    printNPWindow.type = NPWindowTypeDrawable; // Offscreen graphics port as opposed to a proper window
+    
+    // Create embed-mode NPPrint
+    NPPrint npPrint;
+    npPrint.mode = NP_EMBED;
+    npPrint.print.embedPrint.window = printNPWindow;
+    npPrint.print.embedPrint.platformPrint = printGWorld;
+    
+    // Tell the plugin to print into the GWorld
+    [self willCallPlugInFunction];
+    {
+        KJS::JSLock::DropAllLocks dropAllLocks;
+        NPP_Print(plugin, &npPrint);
+    }
+    [self didCallPlugInFunction];
+
+    // Don't need the GWorld anymore
+    DisposeGWorld(printGWorld);
+        
+    return bitmap;
+#endif
+}
+
+- (BOOL)_createAGLContextIfNeeded
+{
+    ASSERT(drawingModel == NPDrawingModelOpenGL);
+
+    // Do nothing (but indicate success) if the AGL context already exists
+    if (aglContext)
+        return YES;
+        
+    switch (window.type) {
+        case NPWindowTypeWindow:
+            return [self _createWindowedAGLContext];
+        
+        case NPWindowTypeDrawable:
+            return [self _createWindowlessAGLContext];
+        
+        default:
+            ASSERT_NOT_REACHED();
+            return NO;
+    }
+}
+
+- (BOOL)_createWindowedAGLContext
+{
+    ASSERT(drawingModel == NPDrawingModelOpenGL);
+    ASSERT(!aglContext);
+    ASSERT(!aglWindow);
+    ASSERT([self window]);
+    
+    GLint pixelFormatAttributes[] = {
+        AGL_RGBA,
+        AGL_RED_SIZE, 8,
+        AGL_GREEN_SIZE, 8,
+        AGL_BLUE_SIZE, 8,
+        AGL_ALPHA_SIZE, 8,
+        AGL_DEPTH_SIZE, 32,
+        AGL_WINDOW,
+        AGL_ACCELERATED,
+        0
+    };
+    
+    // Choose AGL pixel format
+    AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL, 0, pixelFormatAttributes);
+    if (!pixelFormat) {
+        LOG_ERROR("Could not find suitable AGL pixel format: %s", aglErrorString(aglGetError()));
+        return NO;
+    }
+    
+    // Create AGL context
+    aglContext = aglCreateContext(pixelFormat, NULL);
+    aglDestroyPixelFormat(pixelFormat);
+    if (!aglContext) {
+        LOG_ERROR("Could not create AGL context: %s", aglErrorString(aglGetError()));
+        return NO;
+    }
+    
+    // Create AGL window
+    aglWindow = [[NSWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
+    if (!aglWindow) {
+        LOG_ERROR("Could not create window for AGL drawable.");
+        return NO;
+    }
+    
+    // AGL window should allow clicks to go through -- mouse events are tracked by WebCore
+    [aglWindow setIgnoresMouseEvents:YES];
+    
+    // Make sure the window is not opaque -- windowed plug-ins cannot layer with other page elements
+    [aglWindow setOpaque:YES];
+
+    // Position and order in the AGL window
+    [self _reshapeAGLWindow];
+
+    // Attach the AGL context to its window
+    GLboolean success;
+#ifdef AGL_VERSION_3_0
+    success = aglSetWindowRef(aglContext, (WindowRef)[aglWindow windowRef]);
+#else
+    success = aglSetDrawable(aglContext, (AGLDrawable)GetWindowPort((WindowRef)[aglWindow windowRef]));
+#endif
+    if (!success) {
+        LOG_ERROR("Could not set AGL drawable: %s", aglErrorString(aglGetError()));
+        aglDestroyContext(aglContext);
+        aglContext = NULL;
+        return NO;
+    }
+        
+    return YES;
+}
+
+- (BOOL)_createWindowlessAGLContext
+{
+    ASSERT(drawingModel == NPDrawingModelOpenGL);
+    ASSERT(!aglContext);
+    ASSERT(!aglWindow);
+    
+    GLint pixelFormatAttributes[] = {
+        AGL_RGBA,
+        AGL_RED_SIZE, 8,
+        AGL_GREEN_SIZE, 8,
+        AGL_BLUE_SIZE, 8,
+        AGL_ALPHA_SIZE, 8,
+        AGL_DEPTH_SIZE, 32,
+        AGL_OFFSCREEN,
+        0
+    };
+
+    // Choose AGL pixel format
+    AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL, 0, pixelFormatAttributes);
+    if (!pixelFormat) {
+        LOG_ERROR("Could not find suitable AGL pixel format: %s", aglErrorString(aglGetError()));
+        return NO;
+    }
+    
+    // Create AGL context
+    aglContext = aglCreateContext(pixelFormat, NULL);
+    aglDestroyPixelFormat(pixelFormat);
+    if (!aglContext) {
+        LOG_ERROR("Could not create AGL context: %s", aglErrorString(aglGetError()));
+        return NO;
+    }
+    
+    // Create offscreen buffer for AGL context
+    NSSize boundsSize = [self bounds].size;
+    GLvoid *offscreenBuffer = (GLvoid *)malloc(static_cast<size_t>(boundsSize.width * boundsSize.height * 4));
+    if (!offscreenBuffer) {
+        LOG_ERROR("Could not allocate offscreen buffer for AGL context");
+        aglDestroyContext(aglContext);
+        aglContext = NULL;
+        return NO;
+    }
+    
+    // Attach AGL context to offscreen buffer
+    CGLContextObj cglContext = [self _cglContext];
+    CGLError error = CGLSetOffScreen(cglContext, static_cast<long>(boundsSize.width), static_cast<long>(boundsSize.height), static_cast<long>(boundsSize.width * 4), offscreenBuffer);
+    if (error) {
+        LOG_ERROR("Could not set offscreen buffer for AGL context: %d", error);
+        aglDestroyContext(aglContext);
+        aglContext = NULL;
+        return NO;
+    }
+    
+    return YES;
+}
+
+- (CGLContextObj)_cglContext
+{
+    ASSERT(drawingModel == NPDrawingModelOpenGL);
+
+    CGLContextObj cglContext = NULL;
+    if (!aglGetCGLContext(aglContext, (void **)&cglContext) || !cglContext)
+        LOG_ERROR("Could not get CGL context for AGL context: %s", aglErrorString(aglGetError()));
+        
+    return cglContext;
+}
+
+- (BOOL)_getAGLOffscreenBuffer:(GLvoid **)outBuffer width:(GLsizei *)outWidth height:(GLsizei *)outHeight
+{
+    ASSERT(drawingModel == NPDrawingModelOpenGL);
+    
+    if (outBuffer)
+        *outBuffer = NULL;
+    if (outWidth)
+        *outWidth = 0;
+    if (outHeight)
+        *outHeight = 0;
+    
+    // Only windowless plug-ins have offscreen buffers
+    if (window.type != NPWindowTypeDrawable)
+        return NO;
+    
+    CGLContextObj cglContext = [self _cglContext];
+    if (!cglContext)
+        return NO;
+    
+    GLsizei width, height;
+    GLint rowBytes;
+    void *offscreenBuffer = NULL;
+    CGLError error = CGLGetOffScreen(cglContext, &width, &height, &rowBytes, &offscreenBuffer);
+    if (error || !offscreenBuffer) {
+        LOG_ERROR("Could not get offscreen buffer for AGL context: %d", error);
+        return NO;
+    }
+    
+    if (outBuffer)
+        *outBuffer = offscreenBuffer;
+    if (outWidth)
+        *outWidth = width;
+    if (outHeight)
+        *outHeight = height;
+    
+    return YES;
+}
+
+- (void)_destroyAGLContext
+{    
+    ASSERT(drawingModel == NPDrawingModelOpenGL);
+
+    if (!aglContext)
+        return;
+
+    if (aglContext) {
+        // If this is a windowless plug-in, free its offscreen buffer
+        GLvoid *offscreenBuffer;
+        if ([self _getAGLOffscreenBuffer:&offscreenBuffer width:NULL height:NULL])
+            free(offscreenBuffer);
+        
+        // Detach context from the AGL window
+#ifdef AGL_VERSION_3_0
+        aglSetWindowRef(aglContext, NULL);
+#else
+        aglSetDrawable(aglContext, NULL);
+#endif
+        
+        // Destroy the context
+        aglDestroyContext(aglContext);
+        aglContext = NULL;
+    }
+    
+    // Destroy the AGL window
+    if (aglWindow) {
+        [self _hideAGLWindow];
+        aglWindow = nil;
+    }
+}
+
+- (void)_reshapeAGLWindow
+{
+    ASSERT(drawingModel == NPDrawingModelOpenGL);
+    
+    if (!aglContext)
+        return;
+
+    switch (window.type) {
+        case NPWindowTypeWindow:
+        {
+            if (!aglWindow)
+                break;
+                
+            // The AGL window is being reshaped because the plugin view has moved.  Since the view has moved, it will soon redraw.
+            // We want the AGL window to update at the same time as its underlying view.  So, we disable screen updates until the
+            // plugin view's window flushes.
+            NSWindow *browserWindow = [self window];
+            ASSERT(browserWindow);
+            [browserWindow disableScreenUpdatesUntilFlush];
+
+            // Add the AGL window as a child of the main window if necessary
+            if ([aglWindow parentWindow] != browserWindow)
+                [browserWindow addChildWindow:aglWindow ordered:NSWindowAbove];
+            
+            // Update the AGL window frame
+            NSRect aglWindowFrame = [self convertRect:[self visibleRect] toView:nil];
+            aglWindowFrame.origin = [browserWindow convertBaseToScreen:aglWindowFrame.origin];
+            [aglWindow setFrame:aglWindowFrame display:NO];
+            
+            // Update the AGL context
+            aglUpdateContext(aglContext);
+        }
+        break;
+        
+        case NPWindowTypeDrawable:
+        {
+            // Get offscreen buffer; we can skip this step if we don't have one yet
+            GLvoid *offscreenBuffer;
+            GLsizei width, height;
+            if (![self _getAGLOffscreenBuffer:&offscreenBuffer width:&width height:&height] || !offscreenBuffer)
+                break;
+            
+            // Don't resize the offscreen buffer if it's already the same size as the view bounds
+            NSSize boundsSize = [self bounds].size;
+            if (boundsSize.width == width && boundsSize.height == height)
+                break;
+            
+            // Resize the offscreen buffer
+            offscreenBuffer = realloc(offscreenBuffer, static_cast<size_t>(boundsSize.width * boundsSize.height * 4));
+            if (!offscreenBuffer) {
+                LOG_ERROR("Could not allocate offscreen buffer for AGL context");
+                break;
+            }
+
+            // Update the offscreen 
+            CGLContextObj cglContext = [self _cglContext];
+            CGLError error = CGLSetOffScreen(cglContext, static_cast<long>(boundsSize.width), static_cast<long>(boundsSize.height), static_cast<long>(boundsSize.width * 4), offscreenBuffer);
+            if (error) {
+                LOG_ERROR("Could not set offscreen buffer for AGL context: %d", error);
+                break;
+            }
+
+            // Update the AGL context
+            aglUpdateContext(aglContext);
+        }
+        break;
+        
+        default:
+            ASSERT_NOT_REACHED();
+        break;
+    }
+}
+
+- (void)_hideAGLWindow
+{
+    ASSERT(drawingModel == NPDrawingModelOpenGL);
+    
+    if (!aglWindow)
+        return;
+    
+    // aglWindow should only be set for a windowed OpenGL plug-in
+    ASSERT(window.type == NPWindowTypeWindow);
+    
+    NSWindow *parentWindow = [aglWindow parentWindow];
+    if (parentWindow) {
+        // Disable screen updates so that this AGL window orders out atomically with other plugins' AGL windows
+        [parentWindow disableScreenUpdatesUntilFlush];
+        ASSERT(parentWindow == [self window]);
+        [parentWindow removeChildWindow:aglWindow];
+    }
+    [aglWindow orderOut:nil];
+}
+
+- (NSImage *)_aglOffscreenImageForDrawingInRect:(NSRect)drawingInRect
+{
+    ASSERT(drawingModel == NPDrawingModelOpenGL);
+
+    CGLContextObj cglContext = [self _cglContext];
+    if (!cglContext)
+        return nil;
+
+    // Get the offscreen buffer
+    GLvoid *offscreenBuffer;
+    GLsizei width, height;
+    if (![self _getAGLOffscreenBuffer:&offscreenBuffer width:&width height:&height])
+        return nil;
+
+    unsigned char *plane = (unsigned char *)offscreenBuffer;
+
+#if defined(__i386__) || defined(__x86_64__)
+    // Make rect inside the offscreen buffer because we're about to directly modify the bits inside drawingInRect
+    NSRect rect = NSIntegralRect(NSIntersectionRect(drawingInRect, NSMakeRect(0, 0, width, height)));
+
+    // The offscreen buffer, being an OpenGL framebuffer, is in BGRA format on x86.  We need to swap the blue and red channels before
+    // wrapping the buffer in an NSBitmapImageRep, which only supports RGBA and ARGB.
+    // On PowerPC, the OpenGL framebuffer is in ARGB format.  Since that is a format that NSBitmapImageRep supports, all that is
+    // needed on PowerPC is to pass the NSAlphaFirstBitmapFormat flag when creating the NSBitmapImageRep.  On x86, we need to swap the
+    // framebuffer color components such that they are in ARGB order, as they are on PowerPC.
+    // 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.
+    // Since we know what region of the image will ultimately be drawn to screen (drawingInRect), we restrict the channel swapping to
+    // just that region within the offscreen buffer.
+    if (!WebConvertBGRAToARGB(plane, width * 4, (int)rect.origin.x, (int)rect.origin.y, (int)rect.size.width, (int)rect.size.height))
+        return nil;
+#endif /* defined(__i386__) || defined(__x86_64__) */
+    
+    NSBitmapImageRep *aglBitmap = [[NSBitmapImageRep alloc]
+        initWithBitmapDataPlanes:&plane
+                      pixelsWide:width
+                      pixelsHigh:height
+                   bitsPerSample:8
+                 samplesPerPixel:4
+                        hasAlpha:YES
+                        isPlanar:NO
+                  colorSpaceName:NSDeviceRGBColorSpace
+                    bitmapFormat:NSAlphaFirstBitmapFormat
+                     bytesPerRow:width * 4
+                    bitsPerPixel:32];
+    if (!aglBitmap) {
+        LOG_ERROR("Could not create bitmap for AGL offscreen buffer");
+        return nil;
+    }
+
+    // Wrap the bitmap in an NSImage.  This allocation isn't very expensive -- the actual image data is already in the bitmap rep
+    NSImage *aglImage = [[[NSImage alloc] initWithSize:[aglBitmap size]] autorelease];
+    [aglImage addRepresentation:aglBitmap];
+    [aglBitmap release];
+    
+    return aglImage;
+}
+
+- (void)_redeliverStream
+{
+    if ([self dataSource] && [self isStarted]) {
+        // Deliver what has not been passed to the plug-in up to this point.
+        if (_dataLengthReceived > 0) {
+            NSData *data = [[[self dataSource] data] subdataWithRange:NSMakeRange(0, _dataLengthReceived)];
+            _dataLengthReceived = 0;
+            [self pluginView:self receivedData:data];
+            if (![[self dataSource] isLoading]) {
+                if (_error)
+                    [self pluginView:self receivedError:_error];
+                else
+                    [self pluginViewFinishedLoading:self];
+            }
+        }
+    }
+}
+
+@end
+
+@implementation NSData (PluginExtras)
+
+- (BOOL)_web_startsWithBlankLine
+{
+    return [self length] > 0 && ((const char *)[self bytes])[0] == '\n';
+}
+
+
+- (NSInteger)_web_locationAfterFirstBlankLine
+{
+    const char *bytes = (const char *)[self bytes];
+    unsigned length = [self length];
+    
+    unsigned i;
+    for (i = 0; i < length - 4; i++) {
+        
+        //  Support for Acrobat. It sends "\n\n".
+        if (bytes[i] == '\n' && bytes[i+1] == '\n') {
+            return i+2;
+        }
+        
+        // Returns the position after 2 CRLF's or 1 CRLF if it is the first line.
+        if (bytes[i] == '\r' && bytes[i+1] == '\n') {
+            i += 2;
+            if (i == 2) {
+                return i;
+            } else if (bytes[i] == '\n') {
+                // Support for Director. It sends "\r\n\n" (3880387).
+                return i+1;
+            } else if (bytes[i] == '\r' && bytes[i+1] == '\n') {
+                // Support for Flash. It sends "\r\n\r\n" (3758113).
+                return i+2;
+            }
+        }
+    }
+    return NSNotFound;
+}
+
+@end
+#endif