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