64ef4e624d9aa58f46ddd4df09dd9baa450c1079
[WebKit-https.git] / WebKit / Plugins / WebBaseNetscapePluginView.m
1 /*
2  * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #import <WebKit/WebBaseNetscapePluginView.h>
30
31 #import <WebKit/WebAssertions.h>
32 #import <WebKit/WebFrameBridge.h>
33 #import <WebKit/WebDataSource.h>
34 #import <WebKit/WebDefaultUIDelegate.h>
35 #import <WebKit/WebFrameInternal.h> 
36 #import <WebKit/WebFrameView.h>
37 #import <WebKit/WebKitLogging.h>
38 #import <WebKit/WebKitNSStringExtras.h>
39 #import <WebKit/WebNetscapePluginStream.h>
40 #import <WebKit/WebNullPluginView.h>
41 #import <WebKit/WebNSDataExtras.h>
42 #import <WebKit/WebNSDictionaryExtras.h>
43 #import <WebKit/WebNSObjectExtras.h>
44 #import <WebKit/WebNSURLExtras.h>
45 #import <WebKit/WebNSURLRequestExtras.h>
46 #import <WebKit/WebNSViewExtras.h>
47 #import <WebKit/WebNetscapePluginPackage.h>
48 #import <WebKit/WebPreferences.h>
49 #import <WebKit/WebViewPrivate.h>
50 #import <WebKit/WebUIDelegate.h>
51 #import <WebKitSystemInterface.h>
52 #import <JavaScriptCore/npruntime_impl.h>
53
54 #import <Carbon/Carbon.h>
55
56 #import <objc/objc-runtime.h>
57
58 // Send null events 50 times a second when active, so plug-ins like Flash get high frame rates.
59 #define NullEventIntervalActive         0.02
60 #define NullEventIntervalNotActive      0.25
61
62 #define LoginWindowDidSwitchFromUserNotification    @"WebLoginWindowDidSwitchFromUserNotification"
63 #define LoginWindowDidSwitchToUserNotification      @"WebLoginWindowDidSwitchToUserNotification"
64
65
66 static WebBaseNetscapePluginView *currentPluginView = nil;
67
68 typedef struct {
69     GrafPtr oldPort;
70     Point oldOrigin;
71     RgnHandle oldClipRegion;
72     RgnHandle oldVisibleRegion;
73     RgnHandle clipRegion;
74     BOOL forUpdate;
75 } PortState;
76
77 @interface WebPluginRequest : NSObject
78 {
79     NSURLRequest *_request;
80     NSString *_frameName;
81     void *_notifyData;
82     BOOL _didStartFromUserGesture;
83     BOOL _sendNotification;
84 }
85
86 - (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture;
87
88 - (NSURLRequest *)request;
89 - (NSString *)frameName;
90 - (void *)notifyData;
91 - (BOOL)isCurrentEventUserGesture;
92 - (BOOL)sendNotification;
93
94 @end
95
96 @interface NSData (WebPluginDataExtras)
97 - (BOOL)_web_startsWithBlankLine;
98 - (unsigned)_web_locationAfterFirstBlankLine;
99 @end
100
101 static OSStatus TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *pluginView);
102
103 @interface WebBaseNetscapePluginView (ForwardDeclarations)
104 - (void)setWindowIfNecessary;
105 @end
106
107 @implementation WebBaseNetscapePluginView
108
109 + (void)initialize
110 {
111         WKSendUserChangeNotifications();
112 }
113
114 #pragma mark EVENTS
115
116 + (void)getCarbonEvent:(EventRecord *)carbonEvent
117 {
118     carbonEvent->what = nullEvent;
119     carbonEvent->message = 0;
120     carbonEvent->when = TickCount();
121     GetGlobalMouse(&carbonEvent->where);
122     carbonEvent->modifiers = GetCurrentKeyModifiers();
123     if (!Button())
124         carbonEvent->modifiers |= btnState;
125 }
126
127 - (void)getCarbonEvent:(EventRecord *)carbonEvent
128 {
129     [[self class] getCarbonEvent:carbonEvent];
130 }
131
132 - (EventModifiers)modifiersForEvent:(NSEvent *)event
133 {
134     EventModifiers modifiers;
135     unsigned int modifierFlags = [event modifierFlags];
136     NSEventType eventType = [event type];
137     
138     modifiers = 0;
139     
140     if (eventType != NSLeftMouseDown && eventType != NSRightMouseDown)
141         modifiers |= btnState;
142     
143     if (modifierFlags & NSCommandKeyMask)
144         modifiers |= cmdKey;
145     
146     if (modifierFlags & NSShiftKeyMask)
147         modifiers |= shiftKey;
148
149     if (modifierFlags & NSAlphaShiftKeyMask)
150         modifiers |= alphaLock;
151
152     if (modifierFlags & NSAlternateKeyMask)
153         modifiers |= optionKey;
154
155     if (modifierFlags & NSControlKeyMask || eventType == NSRightMouseDown)
156         modifiers |= controlKey;
157     
158     return modifiers;
159 }
160
161 - (void)getCarbonEvent:(EventRecord *)carbonEvent withEvent:(NSEvent *)cocoaEvent
162 {
163     if (WKConvertNSEventToCarbonEvent(carbonEvent, cocoaEvent)) {
164         return;
165     }
166     
167     NSPoint where = [[cocoaEvent window] convertBaseToScreen:[cocoaEvent locationInWindow]];
168         
169     carbonEvent->what = nullEvent;
170     carbonEvent->message = 0;
171     carbonEvent->when = (UInt32)([cocoaEvent timestamp] * 60); // seconds to ticks
172     carbonEvent->where.h = (short)where.x;
173     carbonEvent->where.v = (short)(NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - where.y);
174     carbonEvent->modifiers = [self modifiersForEvent:cocoaEvent];
175 }
176
177 - (BOOL)superviewsHaveSuperviews
178 {
179     NSView *contentView = [[self window] contentView];
180     NSView *view;
181     for (view = self; view != nil; view = [view superview]) { 
182         if (view == contentView) {
183             return YES;
184         }
185     }
186     return NO;
187 }
188
189 // The WindowRef created by -[NSWindow windowRef] has a QuickDraw GrafPort that covers 
190 // the entire window frame (or structure region to use the Carbon term) rather then just the window content.
191 // We can remove this when <rdar://problem/4201099> is fixed.
192 - (void)fixWindowPort
193 {
194     NSWindow *currentWindow = [self currentWindow];
195     if ([currentWindow isKindOfClass:objc_getClass("NSCarbonWindow")])
196         return;
197     
198     float windowHeight = [currentWindow frame].size.height;
199     NSView *contentView = [currentWindow contentView];
200     NSRect contentRect = [contentView convertRect:[contentView frame] toView:nil]; // convert to window-relative coordinates
201     
202     CGrafPtr oldPort;
203     GetPort(&oldPort);    
204     SetPort(GetWindowPort([currentWindow windowRef]));
205     
206     MovePortTo(contentRect.origin.x, /* Flip Y */ windowHeight - NSMaxY(contentRect));
207     PortSize(contentRect.size.width, contentRect.size.height);
208     
209     SetPort(oldPort);
210 }
211
212 - (PortState)saveAndSetPortStateForUpdate:(BOOL)forUpdate
213 {
214     ASSERT([self currentWindow] != nil);
215  
216     [self fixWindowPort];
217    
218     WindowRef windowRef = [[self currentWindow] windowRef];
219     CGrafPtr port = GetWindowPort(windowRef);
220         
221     Rect portBounds;
222     GetPortBounds(port, &portBounds);
223
224     // Use AppKit to convert view coordinates to NSWindow coordinates.
225     NSRect boundsInWindow = [self convertRect:[self bounds] toView:nil];
226     NSRect visibleRectInWindow = [self convertRect:[self visibleRect] toView:nil];
227     
228     // Flip Y to convert NSWindow coordinates to top-left-based window coordinates.
229     float borderViewHeight = [[self currentWindow] frame].size.height;
230     boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow);
231     visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow);
232     
233     // Look at the Carbon port to convert top-left-based window coordinates into top-left-based content coordinates.
234     PixMap *pix = *GetPortPixMap(port);
235     boundsInWindow.origin.x += pix->bounds.left - portBounds.left;
236     boundsInWindow.origin.y += pix->bounds.top - portBounds.top;
237     visibleRectInWindow.origin.x += pix->bounds.left - portBounds.left;
238     visibleRectInWindow.origin.y += pix->bounds.top - portBounds.top;
239     
240     // Set up NS_Port.
241     nPort.port = port;
242     nPort.portx = (int32)-boundsInWindow.origin.x;
243     nPort.porty = (int32)-boundsInWindow.origin.y;
244     
245     // Set up NPWindow.
246     window.window = &nPort;
247     
248     window.x = (int32)boundsInWindow.origin.x; 
249     window.y = (int32)boundsInWindow.origin.y;
250     window.width = NSWidth(boundsInWindow);
251     window.height = NSHeight(boundsInWindow);
252     
253     // "Clip-out" the plug-in when:
254     // 1) it's not really in a window or off-screen or has no height or width.
255     // 2) window.x is a "big negative number" which is how WebCore expresses off-screen widgets.
256     // 3) the window is miniaturized or the app is hidden
257     // 4) we're inside of viewWillMoveToWindow: with a nil window. In this case, superviews may already have nil 
258     // superviews and nil windows and results from convertRect:toView: are incorrect.
259     NSWindow *realWindow = [self window];
260     if (window.width <= 0 || window.height <= 0 || window.x < -100000
261             || realWindow == nil || [realWindow isMiniaturized]
262             || [NSApp isHidden]
263             || ![self superviewsHaveSuperviews]
264             || [self isHiddenOrHasHiddenAncestor]) {
265         // The following code tries to give plug-ins the same size they will eventually have.
266         // The specifiedWidth and specifiedHeight variables are used to predict the size that
267         // WebCore will eventually resize us to.
268
269         // The QuickTime plug-in has problems if you give it a width or height of 0.
270         // Since other plug-ins also might have the same sort of trouble, we make sure
271         // to always give plug-ins a size other than 0,0.
272
273         if (window.width <= 0) {
274             window.width = specifiedWidth > 0 ? specifiedWidth : 100;
275         }
276         if (window.height <= 0) {
277             window.height = specifiedHeight > 0 ? specifiedHeight : 100;
278         }
279
280         window.clipRect.bottom = window.clipRect.top;
281         window.clipRect.left = window.clipRect.right;
282     } else {
283         window.clipRect.top = (uint16)visibleRectInWindow.origin.y;
284         window.clipRect.left = (uint16)visibleRectInWindow.origin.x;
285         window.clipRect.bottom = (uint16)(visibleRectInWindow.origin.y + visibleRectInWindow.size.height);
286         window.clipRect.right = (uint16)(visibleRectInWindow.origin.x + visibleRectInWindow.size.width);        
287     }
288
289     window.type = NPWindowTypeWindow;
290     
291     // Save the port state.
292     PortState portState;
293     
294     GetPort(&portState.oldPort);    
295
296     portState.oldOrigin.h = portBounds.left;
297     portState.oldOrigin.v = portBounds.top;
298
299     portState.oldClipRegion = NewRgn();
300     GetPortClipRegion(port, portState.oldClipRegion);
301     
302     portState.oldVisibleRegion = NewRgn();
303     GetPortVisibleRegion(port, portState.oldVisibleRegion);
304     
305     RgnHandle clipRegion = NewRgn();
306     portState.clipRegion = clipRegion;
307     
308     MacSetRectRgn(clipRegion,
309         window.clipRect.left + nPort.portx, window.clipRect.top + nPort.porty,
310         window.clipRect.right + nPort.portx, window.clipRect.bottom + nPort.porty);
311     
312     // Clip to dirty region when updating in "windowless" mode (transparent)
313     if (forUpdate && isTransparent) {
314         RgnHandle viewClipRegion = NewRgn();
315         
316         // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
317         // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
318         // knows about the true set of dirty rects.
319         NSView *opaqueAncestor = [self opaqueAncestor];
320         const NSRect *dirtyRects;
321         int dirtyRectCount, dirtyRectIndex;
322         [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount];
323
324         for (dirtyRectIndex = 0; dirtyRectIndex < dirtyRectCount; dirtyRectIndex++) {
325             NSRect dirtyRect = [self convertRect:dirtyRects[dirtyRectIndex] fromView:opaqueAncestor];
326             if (!NSEqualSizes(dirtyRect.size, NSZeroSize)) {
327                 // Create a region for this dirty rect
328                 RgnHandle dirtyRectRegion = NewRgn();
329                 SetRectRgn(dirtyRectRegion, NSMinX(dirtyRect), NSMinY(dirtyRect), NSMaxX(dirtyRect), NSMaxY(dirtyRect));
330                 
331                 // Union this dirty rect with the rest of the dirty rects
332                 UnionRgn(viewClipRegion, dirtyRectRegion, viewClipRegion);
333                 DisposeRgn(dirtyRectRegion);
334             }
335         }
336     
337         // Intersect the dirty region with the clip region, so that we only draw over dirty parts
338         SectRgn(clipRegion, viewClipRegion, clipRegion);
339         DisposeRgn(viewClipRegion);
340     }
341     
342     portState.forUpdate = forUpdate;
343     
344     // Switch to the port and set it up.
345     SetPort(port);
346
347     PenNormal();
348     ForeColor(blackColor);
349     BackColor(whiteColor);
350     
351     SetOrigin(nPort.portx, nPort.porty);
352
353     SetPortClipRegion(nPort.port, clipRegion);
354
355     if (forUpdate) {
356         // AppKit may have tried to help us by doing a BeginUpdate.
357         // But the invalid region at that level didn't include AppKit's notion of what was not valid.
358         // We reset the port's visible region to counteract what BeginUpdate did.
359         SetPortVisibleRegion(nPort.port, clipRegion);
360
361         // Some plugins do their own BeginUpdate/EndUpdate.
362         // For those, we must make sure that the update region contains the area we want to draw.
363         InvalWindowRgn(windowRef, clipRegion);
364     }
365     
366     return portState;
367 }
368
369 - (PortState)saveAndSetPortState
370 {
371     return [self saveAndSetPortStateForUpdate:NO];
372 }
373
374 - (void)restorePortState:(PortState)portState
375 {
376     ASSERT([self currentWindow]);
377     
378     WindowRef windowRef = [[self currentWindow] windowRef];
379     CGrafPtr port = GetWindowPort(windowRef);
380
381     if (portState.forUpdate) {
382         ValidWindowRgn(windowRef, portState.clipRegion);
383     }
384     
385     SetOrigin(portState.oldOrigin.h, portState.oldOrigin.v);
386
387     SetPortClipRegion(port, portState.oldClipRegion);
388     if (portState.forUpdate) {
389         SetPortVisibleRegion(port, portState.oldVisibleRegion);
390     }
391
392     DisposeRgn(portState.oldClipRegion);
393     DisposeRgn(portState.oldVisibleRegion);
394     DisposeRgn(portState.clipRegion);
395
396     SetPort(portState.oldPort);
397 }
398
399 - (BOOL)sendEvent:(EventRecord *)event
400 {
401     ASSERT([self window]);
402     ASSERT(event);
403    
404     // If at any point the user clicks or presses a key from within a plugin, set the 
405     // currentEventIsUserGesture flag to true. This is important to differentiate legitimate 
406     // window.open() calls;  we still want to allow those.  See rdar://problem/4010765
407     if(event->what == mouseDown || event->what == keyDown || event->what == mouseUp || event->what == autoKey) {
408         currentEventIsUserGesture = YES;
409     }
410     
411     suspendKeyUpEvents = NO;
412     
413     if (!isStarted) {
414         return NO;
415     }
416
417     ASSERT(NPP_HandleEvent);
418     
419     // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
420     // We probably don't want more general reentrancy protection; we are really
421     // protecting only against this one case, which actually comes up when
422     // you first install the SVG viewer plug-in.
423     if (inSetWindow) {
424         return NO;
425     }
426
427     BOOL defers = [[self webView] defersCallbacks];
428     if (!defers) {
429         [[self webView] setDefersCallbacks:YES];
430     }
431
432     PortState portState = [self saveAndSetPortStateForUpdate:event->what == updateEvt];
433     
434     // We may have changed the window, so inform the plug-in.
435     [self setWindowIfNecessary];
436
437 #ifndef NDEBUG
438     // Draw green to help debug.
439     // If we see any green we know something's wrong.
440     if (!isTransparent && event->what == updateEvt) {
441         ForeColor(greenColor);
442         const Rect bigRect = { -10000, -10000, 10000, 10000 };
443         PaintRect(&bigRect);
444         ForeColor(blackColor);
445     }
446 #endif
447     
448     // Temporarily retain self in case the plug-in view is released while sending an event. 
449     [self retain];
450
451     BOOL acceptedEvent = NPP_HandleEvent(instance, event);
452
453     currentEventIsUserGesture = NO;
454     
455     if ([self currentWindow]) {
456         [self restorePortState:portState];
457     }
458
459     if (!defers) {
460         [[self webView] setDefersCallbacks:NO];
461     }
462     
463     [self release];
464     
465     return acceptedEvent;
466 }
467
468 - (void)sendActivateEvent:(BOOL)activate
469 {
470     EventRecord event;
471     
472     [self getCarbonEvent:&event];
473     event.what = activateEvt;
474     WindowRef windowRef = [[self window] windowRef];
475     event.message = (UInt32)windowRef;
476     if (activate) {
477         event.modifiers |= activeFlag;
478     }
479     
480     BOOL acceptedEvent;
481     acceptedEvent = [self sendEvent:&event]; 
482     
483     LOG(PluginEvents, "NPP_HandleEvent(activateEvent): %d  isActive: %d", acceptedEvent, activate);
484 }
485
486 - (BOOL)sendUpdateEvent
487 {
488     EventRecord event;
489     
490     [self getCarbonEvent:&event];
491     event.what = updateEvt;
492     WindowRef windowRef = [[self window] windowRef];
493     event.message = (UInt32)windowRef;
494
495     BOOL acceptedEvent = [self sendEvent:&event];
496
497     LOG(PluginEvents, "NPP_HandleEvent(updateEvt): %d", acceptedEvent);
498
499     return acceptedEvent;
500 }
501
502 -(void)sendNullEvent
503 {
504     EventRecord event;
505
506     [self getCarbonEvent:&event];
507
508     // Plug-in should not react to cursor position when not active or when a menu is down.
509     MenuTrackingData trackingData;
510     OSStatus error = GetMenuTrackingData(NULL, &trackingData);
511
512     // Plug-in should not react to cursor position when the actual window is not key.
513     if (![[self window] isKeyWindow] || (error == noErr && trackingData.menu)) {
514         // FIXME: Does passing a v and h of -1 really prevent it from reacting to the cursor position?
515         event.where.v = -1;
516         event.where.h = -1;
517     }
518
519     [self sendEvent:&event];
520 }
521
522 - (void)stopNullEvents
523 {
524     [nullEventTimer invalidate];
525     [nullEventTimer release];
526     nullEventTimer = nil;
527 }
528
529 - (void)restartNullEvents
530 {
531     ASSERT([self window]);
532     
533     if (nullEventTimer) {
534         [self stopNullEvents];
535     }
536     
537     if ([[self window] isMiniaturized]) {
538         return;
539     }
540
541     NSTimeInterval interval;
542
543     // Send null events less frequently when the actual window is not key.  Also, allow the DB
544     // to override this behavior and send full speed events to non key windows.
545     if ([[self window] isKeyWindow] || [[self webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendActiveNullEventsToPlugIns]) {
546         interval = NullEventIntervalActive;
547     } else {
548         interval = NullEventIntervalNotActive;
549     }
550     
551     nullEventTimer = [[NSTimer scheduledTimerWithTimeInterval:interval
552                                                        target:self
553                                                      selector:@selector(sendNullEvent)
554                                                      userInfo:nil
555                                                       repeats:YES] retain];
556 }
557
558 - (BOOL)acceptsFirstResponder
559 {
560     return YES;
561 }
562
563 - (void)installKeyEventHandler
564 {
565     static const EventTypeSpec sTSMEvents[] =
566     {
567     { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
568     };
569     
570     if (!keyEventHandler) {
571         InstallEventHandler(GetWindowEventTarget([[self window] windowRef]),
572                             NewEventHandlerUPP(TSMEventHandler),
573                             GetEventTypeCount(sTSMEvents),
574                             sTSMEvents,
575                             self,
576                             &keyEventHandler);
577     }
578 }
579
580 - (void)removeKeyEventHandler
581 {
582     if (keyEventHandler) {
583         RemoveEventHandler(keyEventHandler);
584         keyEventHandler = NULL;
585     }
586 }
587
588 - (void)setHasFocus:(BOOL)flag
589 {
590     if (hasFocus != flag) {
591         hasFocus = flag;
592         EventRecord event;
593         [self getCarbonEvent:&event];
594         BOOL acceptedEvent;
595         if (hasFocus) {
596             event.what = getFocusEvent;
597             acceptedEvent = [self sendEvent:&event]; 
598             LOG(PluginEvents, "NPP_HandleEvent(getFocusEvent): %d", acceptedEvent);
599             [self installKeyEventHandler];
600         } else {
601             event.what = loseFocusEvent;
602             acceptedEvent = [self sendEvent:&event]; 
603             LOG(PluginEvents, "NPP_HandleEvent(loseFocusEvent): %d", acceptedEvent);
604             [self removeKeyEventHandler];
605         }
606     }
607 }
608
609 - (BOOL)becomeFirstResponder
610 {
611     [self setHasFocus:YES];
612     return YES;
613 }
614
615 - (BOOL)resignFirstResponder
616 {
617     [self setHasFocus:NO];    
618     return YES;
619 }
620
621 // AppKit doesn't call mouseDown or mouseUp on right-click. Simulate control-click
622 // mouseDown and mouseUp so plug-ins get the right-click event as they do in Carbon (3125743).
623 - (void)rightMouseDown:(NSEvent *)theEvent
624 {
625     [self mouseDown:theEvent];
626 }
627
628 - (void)rightMouseUp:(NSEvent *)theEvent
629 {
630     [self mouseUp:theEvent];
631 }
632
633 - (void)mouseDown:(NSEvent *)theEvent
634 {
635     EventRecord event;
636
637     [self getCarbonEvent:&event withEvent:theEvent];
638     event.what = mouseDown;
639
640     BOOL acceptedEvent;
641     acceptedEvent = [self sendEvent:&event]; 
642     
643     LOG(PluginEvents, "NPP_HandleEvent(mouseDown): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
644 }
645
646 - (void)mouseUp:(NSEvent *)theEvent
647 {
648     EventRecord event;
649     
650     [self getCarbonEvent:&event withEvent:theEvent];
651     event.what = mouseUp;
652
653     BOOL acceptedEvent;
654     acceptedEvent = [self sendEvent:&event]; 
655     
656     LOG(PluginEvents, "NPP_HandleEvent(mouseUp): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
657 }
658
659 - (void)mouseEntered:(NSEvent *)theEvent
660 {
661     EventRecord event;
662     
663     [self getCarbonEvent:&event withEvent:theEvent];
664     event.what = adjustCursorEvent;
665
666     BOOL acceptedEvent;
667     acceptedEvent = [self sendEvent:&event]; 
668     
669     LOG(PluginEvents, "NPP_HandleEvent(mouseEntered): %d", acceptedEvent);
670 }
671
672 - (void)mouseExited:(NSEvent *)theEvent
673 {
674     EventRecord event;
675         
676     [self getCarbonEvent:&event withEvent:theEvent];
677     event.what = adjustCursorEvent;
678
679     BOOL acceptedEvent;
680     acceptedEvent = [self sendEvent:&event]; 
681     
682     LOG(PluginEvents, "NPP_HandleEvent(mouseExited): %d", acceptedEvent);
683     
684     // Set cursor back to arrow cursor.
685     [[NSCursor arrowCursor] set];
686 }
687
688 - (void)mouseDragged:(NSEvent *)theEvent
689 {
690     // Do nothing so that other responders don't respond to the drag that initiated in this view.
691 }
692
693 - (UInt32)keyMessageForEvent:(NSEvent *)event
694 {
695     NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())];
696     if (!data) {
697         return 0;
698     }
699     UInt8 characterCode;
700     [data getBytes:&characterCode length:1];
701     UInt16 keyCode = [event keyCode];
702     return keyCode << 8 | characterCode;
703 }
704
705 - (void)keyUp:(NSEvent *)theEvent
706 {
707         WKSendKeyEventToTSM(theEvent);
708     
709     // TSM won't send keyUp events so we have to send them ourselves.
710     // Only send keyUp events after we receive the TSM callback because this is what plug-in expect from OS 9.
711     if (!suspendKeyUpEvents) {
712         EventRecord event;
713         
714         [self getCarbonEvent:&event withEvent:theEvent];
715         event.what = keyUp;
716         
717         if (event.message == 0) {
718             event.message = [self keyMessageForEvent:theEvent];
719         }
720         
721         [self sendEvent:&event];
722     }
723 }
724
725 - (void)keyDown:(NSEvent *)theEvent
726 {
727     suspendKeyUpEvents = YES;
728     WKSendKeyEventToTSM(theEvent);
729 }
730
731 static OSStatus TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *pluginView)
732 {    
733     EventRef rawKeyEventRef;
734     OSStatus status = GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(EventRef), NULL, &rawKeyEventRef);
735     if (status != noErr) {
736         ERROR("GetEventParameter failed with error: %d", status);
737         return noErr;
738     }
739     
740     // Two-pass read to allocate/extract Mac charCodes
741     UInt32 numBytes;    
742     status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL);
743     if (status != noErr) {
744         ERROR("GetEventParameter failed with error: %d", status);
745         return noErr;
746     }
747     char *buffer = malloc(numBytes);
748     status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, numBytes, NULL, buffer);
749     if (status != noErr) {
750         ERROR("GetEventParameter failed with error: %d", status);
751         free(buffer);
752         return noErr;
753     }
754     
755     EventRef cloneEvent = CopyEvent(rawKeyEventRef);
756     unsigned i;
757     for (i = 0; i < numBytes; i++) {
758         status = SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes, typeChar, 1 /* one char code */, &buffer[i]);
759         if (status != noErr) {
760             ERROR("SetEventParameter failed with error: %d", status);
761             free(buffer);
762             return noErr;
763         }
764         
765         EventRecord eventRec;
766         if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
767             BOOL acceptedEvent;
768             acceptedEvent = [(WebBaseNetscapePluginView *)pluginView sendEvent:&eventRec];
769             
770             LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu",
771                 acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask));
772             
773             // We originally thought that if the plug-in didn't accept this event,
774             // we should pass it along so that keyboard scrolling, for example, will work.
775             // In practice, this is not a good idea, because plug-ins tend to eat the event but return false.
776             // MacIE handles each key event twice because of this, but we will emulate the other browsers instead.
777         }
778     }
779     ReleaseEvent(cloneEvent);
780     
781     free(buffer);
782     return noErr;
783 }
784
785 // Fake up command-modified events so cut, copy, paste and select all menus work.
786 - (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character
787 {
788     EventRecord event;
789     [self getCarbonEvent:&event];
790     event.what = keyDown;
791     event.modifiers |= cmdKey;
792     event.message = keyCode << 8 | character;
793     [self sendEvent:&event];
794 }
795
796 - (void)cut:(id)sender
797 {
798     [self sendModifierEventWithKeyCode:7 character:'x'];
799 }
800
801 - (void)copy:(id)sender
802 {
803     [self sendModifierEventWithKeyCode:8 character:'c'];
804 }
805
806 - (void)paste:(id)sender
807 {
808     [self sendModifierEventWithKeyCode:9 character:'v'];
809 }
810
811 - (void)selectAll:(id)sender
812 {
813     [self sendModifierEventWithKeyCode:0 character:'a'];
814 }
815
816 #pragma mark WEB_NETSCAPE_PLUGIN
817
818 - (BOOL)isNewWindowEqualToOldWindow
819 {
820     if (window.x != lastSetWindow.x) {
821         return NO;
822     }
823     if (window.y != lastSetWindow.y) {
824         return NO;
825     }
826     if (window.width != lastSetWindow.width) {
827         return NO;
828     }
829     if (window.height != lastSetWindow.height) {
830         return NO;
831     }
832     if (window.clipRect.top != lastSetWindow.clipRect.top) {
833         return NO;
834     }
835     if (window.clipRect.left != lastSetWindow.clipRect.left) {
836         return NO;
837     }
838     if (window.clipRect.bottom  != lastSetWindow.clipRect.bottom ) {
839         return NO;
840     }
841     if (window.clipRect.right != lastSetWindow.clipRect.right) {
842         return NO;
843     }
844     if (window.type != lastSetWindow.type) {
845         return NO;
846     }
847     if (nPort.portx != lastSetPort.portx) {
848         return NO;
849     }
850     if (nPort.porty != lastSetPort.porty) {
851         return NO;
852     }
853     if (nPort.port != lastSetPort.port) {
854         return NO;
855     }
856     
857     return YES;
858 }
859
860 - (void)updateAndSetWindow
861 {    
862     PortState portState = [self saveAndSetPortState];
863     [self setWindowIfNecessary];
864     [self restorePortState:portState];
865 }
866
867 - (void)setWindowIfNecessary
868 {
869     if (!isStarted) {
870         return;
871     }
872     
873     if (![self isNewWindowEqualToOldWindow]) {        
874         // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
875         // We probably don't want more general reentrancy protection; we are really
876         // protecting only against this one case, which actually comes up when
877         // you first install the SVG viewer plug-in.
878         NPError npErr;
879         ASSERT(!inSetWindow);
880         
881         inSetWindow = YES;
882         npErr = NPP_SetWindow(instance, &window);
883         inSetWindow = NO;
884
885         LOG(Plugins, "NPP_SetWindow: %d, port=0x%08x, window.x:%d window.y:%d",
886             npErr, (int)nPort.port, (int)window.x, (int)window.y);
887         
888         lastSetWindow = window;
889         lastSetPort = nPort;
890     }
891 }
892
893 - (void)removeTrackingRect
894 {
895     if (trackingTag) {
896         [self removeTrackingRect:trackingTag];
897         trackingTag = 0;
898
899         // Must release the window to balance the retain in resetTrackingRect.
900         // But must do it after setting trackingTag to 0 so we don't re-enter.
901         [[self window] release];
902     }
903 }
904
905 - (void)resetTrackingRect
906 {
907     [self removeTrackingRect];
908     if (isStarted) {
909         // Must retain the window so that removeTrackingRect can work after the window is closed.
910         [[self window] retain];
911         trackingTag = [self addTrackingRect:[self bounds] owner:self userData:nil assumeInside:NO];
912     }
913 }
914
915 + (void)setCurrentPluginView:(WebBaseNetscapePluginView *)view
916 {
917     currentPluginView = view;
918 }
919
920 + (WebBaseNetscapePluginView *)currentPluginView
921 {
922     return currentPluginView;
923 }
924
925 - (BOOL)canStart
926 {
927     return YES;
928 }
929
930 - (void)didStart
931 {
932     // Do nothing. Overridden by subclasses.
933 }
934
935 - (void)addWindowObservers
936 {
937     ASSERT([self window]);
938
939     NSWindow *theWindow = [self window];
940     
941     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
942     NSView *view;
943     for (view = self; view; view = [view superview]) {
944         [notificationCenter addObserver:self selector:@selector(viewHasMoved:)
945                                    name:NSViewFrameDidChangeNotification object:view];
946         [notificationCenter addObserver:self selector:@selector(viewHasMoved:)
947                                    name:NSViewBoundsDidChangeNotification object:view];
948     }
949     [notificationCenter addObserver:self selector:@selector(windowWillClose:)
950                                name:NSWindowWillCloseNotification object:theWindow];
951     [notificationCenter addObserver:self selector:@selector(windowBecameKey:)
952                                name:NSWindowDidBecomeKeyNotification object:theWindow];
953     [notificationCenter addObserver:self selector:@selector(windowResignedKey:)
954                                name:NSWindowDidResignKeyNotification object:theWindow];
955     [notificationCenter addObserver:self selector:@selector(windowDidMiniaturize:)
956                                name:NSWindowDidMiniaturizeNotification object:theWindow];
957     [notificationCenter addObserver:self selector:@selector(windowDidDeminiaturize:)
958                                name:NSWindowDidDeminiaturizeNotification object:theWindow];
959     
960     [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchFromUser:)
961                                name:LoginWindowDidSwitchFromUserNotification object:nil];
962     [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchToUser:)
963                                name:LoginWindowDidSwitchToUserNotification object:nil];
964 }
965
966 - (void)removeWindowObservers
967 {
968     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
969     [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification     object:nil];
970     [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification    object:nil];
971     [notificationCenter removeObserver:self name:NSWindowWillCloseNotification        object:nil];
972     [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification     object:nil];
973     [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification     object:nil];
974     [notificationCenter removeObserver:self name:NSWindowDidMiniaturizeNotification   object:nil];
975     [notificationCenter removeObserver:self name:NSWindowDidDeminiaturizeNotification object:nil];
976     [notificationCenter removeObserver:self name:LoginWindowDidSwitchFromUserNotification   object:nil];
977     [notificationCenter removeObserver:self name:LoginWindowDidSwitchToUserNotification     object:nil];
978 }
979
980 - (BOOL)start
981 {
982     ASSERT([self currentWindow]);
983     
984     if (isStarted) {
985         return YES;
986     }
987
988     if (![self canStart]) {
989         return NO;
990     }
991     
992     ASSERT([self webView]);
993     
994     if (![[[self webView] preferences] arePlugInsEnabled]) {
995         return NO;
996     }
997
998     ASSERT(NPP_New);
999
1000     [[self class] setCurrentPluginView:self];
1001     NPError npErr = NPP_New((char *)[MIMEType cString], instance, mode, argsCount, cAttributes, cValues, NULL);
1002     [[self class] setCurrentPluginView:nil];
1003     
1004     LOG(Plugins, "NPP_New: %d", npErr);
1005     if (npErr != NPERR_NO_ERROR) {
1006         ERROR("NPP_New failed with error: %d", npErr);
1007         return NO;
1008     }
1009
1010     isStarted = YES;
1011         
1012     [self updateAndSetWindow];
1013
1014     if ([self window]) {
1015         [self addWindowObservers];
1016         if ([[self window] isKeyWindow]) {
1017             [self sendActivateEvent:YES];
1018         }
1019         [self restartNullEvents];
1020     }
1021
1022     [self resetTrackingRect];
1023     
1024     [self didStart];
1025     
1026     return YES;
1027 }
1028
1029 - (void)stop
1030 {
1031     [self removeTrackingRect];
1032
1033     if (!isStarted) {
1034         return;
1035     }
1036     
1037     isStarted = NO;
1038     
1039     // Stop any active streams
1040     [streams makeObjectsPerformSelector:@selector(stop)];
1041     
1042     // Stop the null events
1043     [self stopNullEvents];
1044
1045     // Set cursor back to arrow cursor
1046     [[NSCursor arrowCursor] set];
1047     
1048     // Stop notifications and callbacks.
1049     [self removeWindowObservers];
1050     [[pendingFrameLoads allKeys] makeObjectsPerformSelector:@selector(_setInternalLoadDelegate:) withObject:nil];
1051     [NSObject cancelPreviousPerformRequestsWithTarget:self];
1052
1053     // Setting the window type to 0 ensures that NPP_SetWindow will be called if the plug-in is restarted.
1054     lastSetWindow.type = 0;
1055     
1056     NPError npErr;
1057     npErr = NPP_Destroy(instance, NULL);
1058     LOG(Plugins, "NPP_Destroy: %d", npErr);
1059
1060     instance->pdata = NULL;
1061     
1062     // We usually remove the key event handler in resignFirstResponder but it is possible that resignFirstResponder 
1063     // may never get called so we can't completely rely on it.
1064     [self removeKeyEventHandler];
1065 }
1066
1067 - (BOOL)isStarted
1068 {
1069     return isStarted;
1070 }
1071
1072 - (WebDataSource *)dataSource
1073 {
1074     // Do nothing. Overridden by subclasses.
1075     return nil;
1076 }
1077
1078 - (WebFrame *)webFrame
1079 {
1080     return [[self dataSource] webFrame];
1081 }
1082
1083 - (WebView *)webView
1084 {
1085     return [[self webFrame] webView];
1086 }
1087
1088 - (NSWindow *)currentWindow
1089 {
1090     return [self window] ? [self window] : [[self webView] hostWindow];
1091 }
1092
1093 - (NPP)pluginPointer
1094 {
1095     return instance;
1096 }
1097
1098 - (WebNetscapePluginPackage *)plugin
1099 {
1100     return plugin;
1101 }
1102
1103 - (void)setPlugin:(WebNetscapePluginPackage *)thePlugin;
1104 {
1105     [thePlugin retain];
1106     [plugin release];
1107     plugin = thePlugin;
1108
1109     NPP_New =           [plugin NPP_New];
1110     NPP_Destroy =       [plugin NPP_Destroy];
1111     NPP_SetWindow =     [plugin NPP_SetWindow];
1112     NPP_NewStream =     [plugin NPP_NewStream];
1113     NPP_WriteReady =    [plugin NPP_WriteReady];
1114     NPP_Write =         [plugin NPP_Write];
1115     NPP_StreamAsFile =  [plugin NPP_StreamAsFile];
1116     NPP_DestroyStream = [plugin NPP_DestroyStream];
1117     NPP_HandleEvent =   [plugin NPP_HandleEvent];
1118     NPP_URLNotify =     [plugin NPP_URLNotify];
1119     NPP_GetValue =      [plugin NPP_GetValue];
1120     NPP_SetValue =      [plugin NPP_SetValue];
1121     NPP_Print =         [plugin NPP_Print];
1122 }
1123
1124 - (void)setMIMEType:(NSString *)theMIMEType
1125 {
1126     NSString *type = [theMIMEType copy];
1127     [MIMEType release];
1128     MIMEType = type;
1129 }
1130
1131 - (void)setBaseURL:(NSURL *)theBaseURL
1132 {
1133     [theBaseURL retain];
1134     [baseURL release];
1135     baseURL = theBaseURL;
1136 }
1137
1138 - (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values;
1139 {
1140     ASSERT([keys count] == [values count]);
1141     
1142     // Convert the attributes to 2 C string arrays.
1143     // These arrays are passed to NPP_New, but the strings need to be
1144     // modifiable and live the entire life of the plugin.
1145
1146     // The Java plug-in requires the first argument to be the base URL
1147     if ([MIMEType isEqualToString:@"application/x-java-applet"]) {
1148         cAttributes = (char **)malloc(([keys count] + 1) * sizeof(char *));
1149         cValues = (char **)malloc(([values count] + 1) * sizeof(char *));
1150         cAttributes[0] = strdup("DOCBASE");
1151         cValues[0] = strdup([baseURL _web_URLCString]);
1152         argsCount++;
1153     } else {
1154         cAttributes = (char **)malloc([keys count] * sizeof(char *));
1155         cValues = (char **)malloc([values count] * sizeof(char *));
1156     }
1157
1158     BOOL isWMP = [[[plugin bundle] bundleIdentifier] isEqualToString:@"com.microsoft.WMP.defaultplugin"];
1159     
1160     unsigned i;
1161     unsigned count = [keys count];
1162     for (i = 0; i < count; i++) {
1163         NSString *key = [keys objectAtIndex:i];
1164         NSString *value = [values objectAtIndex:i];
1165         if ([key _webkit_isCaseInsensitiveEqualToString:@"height"]) {
1166             specifiedHeight = [value intValue];
1167         } else if ([key _webkit_isCaseInsensitiveEqualToString:@"width"]) {
1168             specifiedWidth = [value intValue];
1169         }
1170         // Avoid Window Media Player crash when these attributes are present.
1171         if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) {
1172             continue;
1173         }
1174         cAttributes[argsCount] = strdup([key UTF8String]);
1175         cValues[argsCount] = strdup([value UTF8String]);
1176         LOG(Plugins, "%@ = %@", key, value);
1177         argsCount++;
1178     }
1179 }
1180
1181 - (void)setMode:(int)theMode
1182 {
1183     mode = theMode;
1184 }
1185
1186 #pragma mark NSVIEW
1187
1188 - initWithFrame:(NSRect)frame
1189 {
1190     [super initWithFrame:frame];
1191
1192     instance = &instanceStruct;
1193     instance->ndata = self;
1194     streams = [[NSMutableArray alloc] init];
1195     pendingFrameLoads = [[NSMutableDictionary alloc] init];
1196
1197     [[NSNotificationCenter defaultCenter] addObserver:self
1198                                              selector:@selector(preferencesHaveChanged:)
1199                                                  name:WebPreferencesChangedNotification
1200                                                object:nil];
1201
1202     return self;
1203 }
1204
1205 - (void)freeAttributeKeysAndValues
1206 {
1207     unsigned i;
1208     for (i = 0; i < argsCount; i++) {
1209         free(cAttributes[i]);
1210         free(cValues[i]);
1211     }
1212     free(cAttributes);
1213     free(cValues);
1214 }
1215
1216 - (void)dealloc
1217 {
1218     [[NSNotificationCenter defaultCenter] removeObserver:self];
1219     
1220     [self stop];
1221
1222     [plugin release];
1223     [streams release];
1224     [MIMEType release];
1225     [baseURL release];
1226     [pendingFrameLoads release];
1227
1228     [self freeAttributeKeysAndValues];
1229
1230     [super dealloc];
1231 }
1232
1233 - (void)finalize
1234 {
1235     [[NSNotificationCenter defaultCenter] removeObserver:self];
1236
1237     // FIXME: Bad to stop at finalize time. Need to restructure code
1238     // so that we're already stopped before we get to this point.
1239     [self stop];
1240
1241     [self freeAttributeKeysAndValues];
1242
1243     [super finalize];
1244 }
1245
1246 - (void)drawRect:(NSRect)rect
1247 {
1248     if (!isStarted) {
1249         return;
1250     }
1251     
1252     if ([NSGraphicsContext currentContextDrawingToScreen]) {
1253         [self sendUpdateEvent];
1254     } else {
1255         // Printing 2862383
1256     }
1257 }
1258
1259 - (BOOL)isFlipped
1260 {
1261     return YES;
1262 }
1263
1264 -(void)tellQuickTimeToChill
1265 {
1266     // Make a call to the secret QuickDraw API that makes QuickTime calm down.
1267     WindowRef windowRef = [[self window] windowRef];
1268     if (!windowRef) {
1269         return;
1270     }
1271     CGrafPtr port = GetWindowPort(windowRef);
1272     Rect bounds;
1273     GetPortBounds(port, &bounds);
1274         WKCallDrawingNotification(port, &bounds);
1275 }
1276
1277 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
1278 {
1279     [self tellQuickTimeToChill];
1280
1281     // We must remove the tracking rect before we move to the new window.
1282     // Once we move to the new window, it will be too late.
1283     [self removeTrackingRect];
1284     [self removeWindowObservers];
1285     
1286     // Workaround for: <rdar://problem/3822871> resignFirstResponder is not sent to first responder view when it is removed from the window
1287     [self setHasFocus:NO];
1288
1289     if (!newWindow) {
1290         if ([[self webView] hostWindow]) {
1291             // View will be moved out of the actual window but it still has a host window.
1292             [self stopNullEvents];
1293         } else {
1294             // View will have no associated windows.
1295             [self stop];
1296         }
1297     }
1298 }
1299
1300 - (void)viewDidMoveToWindow
1301 {
1302     [self resetTrackingRect];
1303     
1304     if ([self window]) {
1305         // View moved to an actual window. Start it if not already started.
1306         [self start];
1307         [self restartNullEvents];
1308         [self addWindowObservers];
1309     } else if ([[self webView] hostWindow]) {
1310         // View moved out of an actual window, but still has a host window.
1311         // Call setWindow to explicitly "clip out" the plug-in from sight.
1312         // FIXME: It would be nice to do this where we call stopNullEvents in viewWillMoveToWindow.
1313         [self updateAndSetWindow];
1314     }
1315 }
1316
1317 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
1318 {
1319     if (!hostWindow && ![self window]) {
1320         // View will have no associated windows.
1321         [self stop];
1322     }
1323 }
1324
1325 - (void)viewDidMoveToHostWindow
1326 {
1327     if ([[self webView] hostWindow]) {
1328         // View now has an associated window. Start it if not already started.
1329         [self start];
1330     }
1331 }
1332
1333 #pragma mark NOTIFICATIONS
1334
1335 - (void)viewHasMoved:(NSNotification *)notification
1336 {
1337     [self tellQuickTimeToChill];
1338     [self updateAndSetWindow];
1339     [self resetTrackingRect];
1340 }
1341
1342 - (void)windowWillClose:(NSNotification *)notification
1343 {
1344     [self stop];
1345 }
1346
1347 - (void)windowBecameKey:(NSNotification *)notification
1348 {
1349     [self sendActivateEvent:YES];
1350     [self setNeedsDisplay:YES];
1351     [self restartNullEvents];
1352     SetUserFocusWindow([[self window] windowRef]);
1353 }
1354
1355 - (void)windowResignedKey:(NSNotification *)notification
1356 {
1357     [self sendActivateEvent:NO];
1358     [self setNeedsDisplay:YES];
1359     [self restartNullEvents];
1360 }
1361
1362 - (void)windowDidMiniaturize:(NSNotification *)notification
1363 {
1364     [self stopNullEvents];
1365 }
1366
1367 - (void)windowDidDeminiaturize:(NSNotification *)notification
1368 {
1369     [self restartNullEvents];
1370 }
1371
1372 - (void)loginWindowDidSwitchFromUser:(NSNotification *)notification
1373 {
1374     [self stopNullEvents];
1375 }
1376
1377 -(void)loginWindowDidSwitchToUser:(NSNotification *)notification
1378 {
1379     [self restartNullEvents];
1380 }
1381
1382 - (void)preferencesHaveChanged:(NSNotification *)notification
1383 {
1384     WebPreferences *preferences = [[self webView] preferences];
1385     BOOL arePlugInsEnabled = [preferences arePlugInsEnabled];
1386     
1387     if ([notification object] == preferences && isStarted != arePlugInsEnabled) {
1388         if (arePlugInsEnabled) {
1389             if ([self currentWindow]) {
1390                 [self start];
1391             }
1392         } else {
1393             [self stop];
1394             [self setNeedsDisplay:YES];
1395         }
1396     }
1397 }
1398
1399 - (void *)pluginScriptableObject
1400 {
1401     if (NPP_GetValue) {
1402         void *value = 0;
1403         NPError npErr = NPP_GetValue (instance, NPPVpluginScriptableNPObject, (void *)&value);
1404         if (npErr == NPERR_NO_ERROR) {
1405             return value;
1406         }
1407     }
1408     return (void *)0;
1409 }
1410
1411
1412 @end
1413
1414 @implementation WebBaseNetscapePluginView (WebNPPCallbacks)
1415
1416 - (NSMutableURLRequest *)requestWithURLCString:(const char *)URLCString
1417 {
1418     if (!URLCString) {
1419         return nil;
1420     }
1421     
1422     CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, URLCString, kCFStringEncodingWindowsLatin1);
1423     NSString *URLString = [(NSString *)string _web_stringByStrippingReturnCharacters];
1424     NSURL *URL = [NSURL _web_URLWithDataAsString:URLString relativeToURL:baseURL];
1425     CFRelease(string);
1426     if (!URL) {
1427         return nil;
1428     }
1429     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
1430     [request _web_setHTTPReferrer:[[[self webFrame] _bridge] referrer]];
1431     return request;
1432 }
1433
1434 - (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest
1435 {
1436     // FIXME: Is this isStarted check needed here? evaluateJavaScriptPluginRequest should not be called
1437     // if we are stopped since this method is called after a delay and we call 
1438     // cancelPreviousPerformRequestsWithTarget inside of stop.
1439     if (!isStarted) {
1440         return;
1441     }
1442     
1443     NSURL *URL = [[JSPluginRequest request] URL];
1444     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1445     ASSERT(JSString);
1446     
1447     NSString *result = [[[self webFrame] _bridge] stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]];
1448     
1449     // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop.
1450     if (!isStarted) {
1451         return;
1452     }
1453         
1454     if ([JSPluginRequest frameName] != nil) {
1455         // FIXME: If the result is a string, we probably want to put that string into the frame, just
1456         // like we do in KHTMLPartBrowserExtension::openURLRequest.
1457         if ([JSPluginRequest sendNotification]) {
1458             NPP_URLNotify(instance, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]);
1459         }
1460     } else if ([result length] > 0) {
1461         // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does.
1462         NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding];
1463         WebBaseNetscapePluginStream *stream = [[WebBaseNetscapePluginStream alloc] initWithRequestURL:URL
1464                                                                                         pluginPointer:instance
1465                                                                                            notifyData:[JSPluginRequest notifyData]
1466                                                                                      sendNotification:[JSPluginRequest sendNotification]];
1467         [stream startStreamResponseURL:URL
1468                  expectedContentLength:[JSData length]
1469                       lastModifiedDate:nil
1470                               MIMEType:@"text/plain"];
1471         [stream receivedData:JSData];
1472         [stream finishedLoadingWithData:JSData];
1473         [stream release];
1474     }
1475 }
1476
1477 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
1478 {
1479     ASSERT(isStarted);
1480     
1481     WebPluginRequest *pluginRequest = [pendingFrameLoads objectForKey:webFrame];
1482     ASSERT(pluginRequest != nil);
1483     ASSERT([pluginRequest sendNotification]);
1484         
1485     NPP_URLNotify(instance, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]);
1486     
1487     [pendingFrameLoads removeObjectForKey:webFrame];
1488     [webFrame _setInternalLoadDelegate:nil];
1489 }
1490
1491 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
1492 {
1493     NPReason reason = NPRES_DONE;
1494     if (error != nil) {
1495         reason = [WebBaseNetscapePluginStream reasonForError:error];
1496     }    
1497     [self webFrame:webFrame didFinishLoadWithReason:reason];
1498 }
1499
1500 - (void)loadPluginRequest:(WebPluginRequest *)pluginRequest
1501 {
1502     NSURLRequest *request = [pluginRequest request];
1503     NSString *frameName = [pluginRequest frameName];
1504     WebFrame *frame = nil;
1505     
1506     NSURL *URL = [request URL];
1507     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1508     
1509     ASSERT(frameName || JSString);
1510     
1511     if (frameName) {
1512         // FIXME - need to get rid of this window creation which
1513         // bypasses normal targeted link handling
1514         frame = [[self webFrame] findFrameNamed:frameName];
1515     
1516         if (frame == nil) {
1517             WebView *newWebView = nil;
1518             WebView *currentWebView = [self webView];
1519             id wd = [currentWebView UIDelegate];
1520             if ([wd respondsToSelector:@selector(webView:createWebViewWithRequest:)]) {
1521                 newWebView = [wd webView:currentWebView createWebViewWithRequest:nil];
1522             } else {
1523                 newWebView = [[WebDefaultUIDelegate sharedUIDelegate] webView:currentWebView createWebViewWithRequest:nil];
1524             }
1525             
1526             [[[newWebView mainFrame] _bridge] setName:frameName];
1527             [[newWebView _UIDelegateForwarder] webViewShow:newWebView];
1528             frame = [newWebView mainFrame];
1529         }
1530     }
1531
1532     if (JSString) {
1533         ASSERT(frame == nil || [self webFrame] == frame);
1534         [self evaluateJavaScriptPluginRequest:pluginRequest];
1535     } else {
1536         [frame loadRequest:request];
1537         if ([pluginRequest sendNotification]) {
1538             // Check if another plug-in view or even this view is waiting for the frame to load.
1539             // If it is, tell it that the load was cancelled because it will be anyway.
1540             WebBaseNetscapePluginView *view = [frame _internalLoadDelegate];
1541             if (view != nil) {
1542                 ASSERT([view isKindOfClass:[WebBaseNetscapePluginView class]]);
1543                 [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK];
1544             }
1545             [pendingFrameLoads _webkit_setObject:pluginRequest forUncopiedKey:frame];
1546             [frame _setInternalLoadDelegate:self];
1547         }
1548     }
1549 }
1550
1551 - (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification
1552 {
1553     NSURL *URL = [request URL];
1554
1555     if (!URL) {
1556         return NPERR_INVALID_URL;
1557     }
1558     
1559     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1560     if (JSString != nil) {
1561         if (![[[self webView] preferences] isJavaScriptEnabled]) {
1562             // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does.
1563             return NPERR_GENERIC_ERROR;
1564         } else if (cTarget == NULL && mode == NP_FULL) {
1565             // Don't allow a JavaScript request from a standalone plug-in that is self-targetted
1566             // because this can cause the user to be redirected to a blank page (3424039).
1567             return NPERR_INVALID_PARAM;
1568         }
1569     }
1570         
1571     if (cTarget || JSString) {
1572         // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't
1573         // want to potentially kill the plug-in inside of its URL request.
1574         NSString *target = nil;
1575         if (cTarget) {
1576             // Find the frame given the target string.
1577             target = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, cTarget, kCFStringEncodingWindowsLatin1);
1578         }
1579         
1580         WebFrame *frame = [self webFrame];
1581         if (JSString != nil && target != nil && [frame findFrameNamed:target] != frame) {
1582             // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
1583             CFRelease(target);
1584             return NPERR_INVALID_PARAM;
1585         }
1586         
1587         WebPluginRequest *pluginRequest = [[WebPluginRequest alloc] initWithRequest:request frameName:target notifyData:notifyData sendNotification:sendNotification didStartFromUserGesture:currentEventIsUserGesture];
1588         [self performSelector:@selector(loadPluginRequest:) withObject:pluginRequest afterDelay:0];
1589         [pluginRequest release];
1590         if (target) {
1591             CFRelease(target);
1592         }
1593     } else {
1594         WebNetscapePluginStream *stream = [[WebNetscapePluginStream alloc] initWithRequest:request 
1595                                                                              pluginPointer:instance 
1596                                                                                 notifyData:notifyData 
1597                                                                           sendNotification:sendNotification];
1598         if (!stream) {
1599             return NPERR_INVALID_URL;
1600         }
1601         [streams addObject:stream];
1602         [stream start];
1603         [stream release];
1604     }
1605     
1606     return NPERR_NO_ERROR;
1607 }
1608
1609 -(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData
1610 {
1611     LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget);
1612
1613     NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1614     return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES];
1615 }
1616
1617 -(NPError)getURL:(const char *)URLCString target:(const char *)cTarget
1618 {
1619     LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget);
1620
1621     NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1622     return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO];
1623 }
1624
1625 - (NPError)_postURL:(const char *)URLCString
1626              target:(const char *)target
1627                 len:(UInt32)len
1628                 buf:(const char *)buf
1629                file:(NPBool)file
1630          notifyData:(void *)notifyData
1631    sendNotification:(BOOL)sendNotification
1632        allowHeaders:(BOOL)allowHeaders
1633 {
1634     if (!URLCString || !len || !buf) {
1635         return NPERR_INVALID_PARAM;
1636     }
1637     
1638     NSData *postData = nil;
1639
1640     if (file) {
1641         // If we're posting a file, buf is either a file URL or a path to the file.
1642         NSString *bufString = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingWindowsLatin1);
1643         if (!bufString) {
1644             return NPERR_INVALID_PARAM;
1645         }
1646         NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString];
1647         NSString *path;
1648         if ([fileURL isFileURL]) {
1649             path = [fileURL path];
1650         } else {
1651             path = bufString;
1652         }
1653         postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]];
1654         CFRelease(bufString);
1655         if (!postData) {
1656             return NPERR_FILE_NOT_FOUND;
1657         }
1658     } else {
1659         postData = [NSData dataWithBytes:buf length:len];
1660     }
1661
1662     if ([postData length] == 0) {
1663         return NPERR_INVALID_PARAM;
1664     }
1665
1666     NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1667     [request setHTTPMethod:@"POST"];
1668     
1669     if (allowHeaders) {
1670         if ([postData _web_startsWithBlankLine]) {
1671             postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)];
1672         } else {
1673             unsigned location = [postData _web_locationAfterFirstBlankLine];
1674             if (location != NSNotFound) {
1675                 // If the blank line is somewhere in the middle of postData, everything before is the header.
1676                 NSData *headerData = [postData subdataWithRange:NSMakeRange(0, location)];
1677                 NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields];
1678                 unsigned dataLength = [postData length] - location;
1679
1680                 // Sometimes plugins like to set Content-Length themselves when they post,
1681                 // but WebFoundation does not like that. So we will remove the header
1682                 // and instead truncate the data to the requested length.
1683                 NSString *contentLength = [header objectForKey:@"Content-Length"];
1684
1685                 if (contentLength != nil) {
1686                     dataLength = MIN((unsigned)[contentLength intValue], dataLength);
1687                 }
1688                 [header removeObjectForKey:@"Content-Length"];
1689
1690                 if ([header count] > 0) {
1691                     [request setAllHTTPHeaderFields:header];
1692                 }
1693                 // Everything after the blank line is the actual content of the POST.
1694                 postData = [postData subdataWithRange:NSMakeRange(location, dataLength)];
1695
1696             }
1697         }
1698         if ([postData length] == 0) {
1699             return NPERR_INVALID_PARAM;
1700         }
1701     }
1702
1703     // Plug-ins expect to receive uncached data when doing a POST (3347134).
1704     [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
1705     [request setHTTPBody:postData];
1706     
1707     return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification];
1708 }
1709
1710 - (NPError)postURLNotify:(const char *)URLCString
1711                   target:(const char *)target
1712                      len:(UInt32)len
1713                      buf:(const char *)buf
1714                     file:(NPBool)file
1715               notifyData:(void *)notifyData
1716 {
1717     LOG(Plugins, "NPN_PostURLNotify: %s", URLCString);
1718     return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:notifyData sendNotification:YES allowHeaders:YES];
1719 }
1720
1721 -(NPError)postURL:(const char *)URLCString
1722            target:(const char *)target
1723               len:(UInt32)len
1724               buf:(const char *)buf
1725              file:(NPBool)file
1726 {
1727     LOG(Plugins, "NPN_PostURL: %s", URLCString);        
1728     // As documented, only allow headers to be specified via NPP_PostURL when using a file.
1729     return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:NULL sendNotification:NO allowHeaders:file];
1730 }
1731
1732 -(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream
1733 {
1734     LOG(Plugins, "NPN_NewStream");
1735     return NPERR_GENERIC_ERROR;
1736 }
1737
1738 -(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer
1739 {
1740     LOG(Plugins, "NPN_Write");
1741     return NPERR_GENERIC_ERROR;
1742 }
1743
1744 -(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason
1745 {
1746     LOG(Plugins, "NPN_DestroyStream");
1747     if (!stream->ndata) {
1748         return NPERR_INVALID_INSTANCE_ERROR;
1749     }
1750     WebBaseNetscapePluginStream *browserStream = (WebBaseNetscapePluginStream *)stream->ndata;
1751     [browserStream cancelLoadAndDestroyStreamWithError:[browserStream errorForReason:reason]];
1752     return NPERR_NO_ERROR;
1753 }
1754
1755 - (const char *)userAgent
1756 {
1757     return [[[self webView] userAgentForURL:baseURL] lossyCString];
1758 }
1759
1760 -(void)status:(const char *)message
1761 {    
1762     if (!message) {
1763         ERROR("NPN_Status passed a NULL status message");
1764         return;
1765     }
1766
1767     CFStringRef status = CFStringCreateWithCString(NULL, message, kCFStringEncodingWindowsLatin1);
1768     LOG(Plugins, "NPN_Status: %@", status);
1769     WebView *wv = [self webView];
1770     [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status];
1771     CFRelease(status);
1772 }
1773
1774 -(void)invalidateRect:(NPRect *)invalidRect
1775 {
1776     LOG(Plugins, "NPN_InvalidateRect");
1777     [self setNeedsDisplayInRect:NSMakeRect(invalidRect->left, invalidRect->top,
1778         (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)];
1779 }
1780
1781 -(void)invalidateRegion:(NPRegion)invalidRegion
1782 {
1783     LOG(Plugins, "NPN_InvalidateRegion");
1784     Rect invalidRect;
1785     GetRegionBounds(invalidRegion, &invalidRect);
1786     [self setNeedsDisplayInRect:NSMakeRect(invalidRect.left, invalidRect.top,
1787         (float)invalidRect.right - invalidRect.left, (float)invalidRect.bottom - invalidRect.top)];
1788 }
1789
1790 -(void)forceRedraw
1791 {
1792     LOG(Plugins, "forceRedraw");
1793     [self setNeedsDisplay:YES];
1794     [[self window] displayIfNeeded];
1795 }
1796
1797 - (NPError)getVariable:(NPNVariable)variable value:(void *)value
1798 {
1799     if (variable == NPNVWindowNPObject) {
1800         NPObject *windowScriptObject = [[[self webFrame] _bridge] windowScriptNPObject];
1801
1802         // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
1803         if (windowScriptObject)
1804             _NPN_RetainObject(windowScriptObject);
1805         
1806         void **v = (void **)value;
1807         *v = windowScriptObject;
1808         
1809         return NPERR_NO_ERROR;
1810     }
1811     return NPERR_GENERIC_ERROR;
1812 }
1813
1814 - (NPError)setVariable:(NPPVariable)variable value:(void *)value
1815 {
1816     switch (variable) {
1817         case NPPVpluginTransparentBool:
1818         {
1819             BOOL newTransparent = (value != 0);
1820             
1821             // Redisplay if transparency is changing
1822             if (isTransparent != newTransparent)
1823                 [self setNeedsDisplay:YES];
1824             
1825             isTransparent = newTransparent;
1826             
1827             return NPERR_NO_ERROR;
1828         }
1829         
1830         default:
1831             return NPERR_GENERIC_ERROR;
1832     }
1833 }
1834
1835 @end
1836
1837 @implementation WebPluginRequest
1838
1839 - (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture
1840 {
1841     [super init];
1842     _didStartFromUserGesture = currentEventIsUserGesture;
1843     _request = [request retain];
1844     _frameName = [frameName retain];
1845     _notifyData = notifyData;
1846     _sendNotification = sendNotification;
1847     return self;
1848 }
1849
1850 - (void)dealloc
1851 {
1852     [_request release];
1853     [_frameName release];
1854     [super dealloc];
1855 }
1856
1857 - (NSURLRequest *)request
1858 {
1859     return _request;
1860 }
1861
1862 - (NSString *)frameName
1863 {
1864     return _frameName;
1865 }
1866
1867 - (BOOL)isCurrentEventUserGesture
1868 {
1869     return _didStartFromUserGesture;
1870 }
1871
1872 - (BOOL)sendNotification
1873 {
1874     return _sendNotification;
1875 }
1876
1877 - (void *)notifyData
1878 {
1879     return _notifyData;
1880 }
1881
1882 @end
1883
1884 @implementation NSData (PluginExtras)
1885
1886 - (BOOL)_web_startsWithBlankLine
1887 {
1888     return [self length] > 0 && ((const char *)[self bytes])[0] == '\n';
1889 }
1890
1891
1892 - (unsigned)_web_locationAfterFirstBlankLine
1893 {
1894     const char *bytes = (const char *)[self bytes];
1895     unsigned length = [self length];
1896     
1897     unsigned i;
1898     for (i = 0; i < length - 4; i++) {
1899         
1900         //  Support for Acrobat. It sends "\n\n".
1901         if (bytes[i] == '\n' && bytes[i+1] == '\n') {
1902             return i+2;
1903         }
1904         
1905         // Returns the position after 2 CRLF's or 1 CRLF if it is the first line.
1906         if (bytes[i] == '\r' && bytes[i+1] == '\n') {
1907             i += 2;
1908             if (i == 2) {
1909                 return i;
1910             } else if (bytes[i] == '\n') {
1911                 // Support for Director. It sends "\r\n\n" (3880387).
1912                 return i+1;
1913             } else if (bytes[i] == '\r' && bytes[i+1] == '\n') {
1914                 // Support for Flash. It sends "\r\n\r\n" (3758113).
1915                 return i+2;
1916             }
1917         }
1918     }
1919     return NSNotFound;
1920 }
1921
1922 @end