Reviewed by Eric.
[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     if ([[self window] isMiniaturized])
537         return;
538
539     NSTimeInterval interval;
540
541     // Send null events less frequently when the actual window is not key.  Also, allow the DB
542     // to override this behavior and send full speed events to non key windows.
543     // If the plugin is completely obscured (scrolled out of view, for example), then we will
544     // send null events at a reduced rate.
545     if (!isCompletelyObscured && ([[self window] isKeyWindow] || [[self webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendActiveNullEventsToPlugIns]))
546         interval = NullEventIntervalActive;
547     else
548         interval = NullEventIntervalNotActive;
549     
550     nullEventTimer = [[NSTimer scheduledTimerWithTimeInterval:interval
551                                                        target:self
552                                                      selector:@selector(sendNullEvent)
553                                                      userInfo:nil
554                                                       repeats:YES] retain];
555 }
556
557 - (BOOL)acceptsFirstResponder
558 {
559     return YES;
560 }
561
562 - (void)installKeyEventHandler
563 {
564     static const EventTypeSpec sTSMEvents[] =
565     {
566     { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
567     };
568     
569     if (!keyEventHandler) {
570         InstallEventHandler(GetWindowEventTarget([[self window] windowRef]),
571                             NewEventHandlerUPP(TSMEventHandler),
572                             GetEventTypeCount(sTSMEvents),
573                             sTSMEvents,
574                             self,
575                             &keyEventHandler);
576     }
577 }
578
579 - (void)removeKeyEventHandler
580 {
581     if (keyEventHandler) {
582         RemoveEventHandler(keyEventHandler);
583         keyEventHandler = NULL;
584     }
585 }
586
587 - (void)setHasFocus:(BOOL)flag
588 {
589     if (hasFocus != flag) {
590         hasFocus = flag;
591         EventRecord event;
592         [self getCarbonEvent:&event];
593         BOOL acceptedEvent;
594         if (hasFocus) {
595             event.what = getFocusEvent;
596             acceptedEvent = [self sendEvent:&event]; 
597             LOG(PluginEvents, "NPP_HandleEvent(getFocusEvent): %d", acceptedEvent);
598             [self installKeyEventHandler];
599         } else {
600             event.what = loseFocusEvent;
601             acceptedEvent = [self sendEvent:&event]; 
602             LOG(PluginEvents, "NPP_HandleEvent(loseFocusEvent): %d", acceptedEvent);
603             [self removeKeyEventHandler];
604         }
605     }
606 }
607
608 - (BOOL)becomeFirstResponder
609 {
610     [self setHasFocus:YES];
611     return YES;
612 }
613
614 - (BOOL)resignFirstResponder
615 {
616     [self setHasFocus:NO];    
617     return YES;
618 }
619
620 // AppKit doesn't call mouseDown or mouseUp on right-click. Simulate control-click
621 // mouseDown and mouseUp so plug-ins get the right-click event as they do in Carbon (3125743).
622 - (void)rightMouseDown:(NSEvent *)theEvent
623 {
624     [self mouseDown:theEvent];
625 }
626
627 - (void)rightMouseUp:(NSEvent *)theEvent
628 {
629     [self mouseUp:theEvent];
630 }
631
632 - (void)mouseDown:(NSEvent *)theEvent
633 {
634     EventRecord event;
635
636     [self getCarbonEvent:&event withEvent:theEvent];
637     event.what = mouseDown;
638
639     BOOL acceptedEvent;
640     acceptedEvent = [self sendEvent:&event]; 
641     
642     LOG(PluginEvents, "NPP_HandleEvent(mouseDown): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
643 }
644
645 - (void)mouseUp:(NSEvent *)theEvent
646 {
647     EventRecord event;
648     
649     [self getCarbonEvent:&event withEvent:theEvent];
650     event.what = mouseUp;
651
652     BOOL acceptedEvent;
653     acceptedEvent = [self sendEvent:&event]; 
654     
655     LOG(PluginEvents, "NPP_HandleEvent(mouseUp): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
656 }
657
658 - (void)mouseEntered:(NSEvent *)theEvent
659 {
660     EventRecord event;
661     
662     [self getCarbonEvent:&event withEvent:theEvent];
663     event.what = adjustCursorEvent;
664
665     BOOL acceptedEvent;
666     acceptedEvent = [self sendEvent:&event]; 
667     
668     LOG(PluginEvents, "NPP_HandleEvent(mouseEntered): %d", acceptedEvent);
669 }
670
671 - (void)mouseExited:(NSEvent *)theEvent
672 {
673     EventRecord event;
674         
675     [self getCarbonEvent:&event withEvent:theEvent];
676     event.what = adjustCursorEvent;
677
678     BOOL acceptedEvent;
679     acceptedEvent = [self sendEvent:&event]; 
680     
681     LOG(PluginEvents, "NPP_HandleEvent(mouseExited): %d", acceptedEvent);
682     
683     // Set cursor back to arrow cursor.
684     [[NSCursor arrowCursor] set];
685 }
686
687 - (void)mouseDragged:(NSEvent *)theEvent
688 {
689     // Do nothing so that other responders don't respond to the drag that initiated in this view.
690 }
691
692 - (UInt32)keyMessageForEvent:(NSEvent *)event
693 {
694     NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())];
695     if (!data) {
696         return 0;
697     }
698     UInt8 characterCode;
699     [data getBytes:&characterCode length:1];
700     UInt16 keyCode = [event keyCode];
701     return keyCode << 8 | characterCode;
702 }
703
704 - (void)keyUp:(NSEvent *)theEvent
705 {
706         WKSendKeyEventToTSM(theEvent);
707     
708     // TSM won't send keyUp events so we have to send them ourselves.
709     // Only send keyUp events after we receive the TSM callback because this is what plug-in expect from OS 9.
710     if (!suspendKeyUpEvents) {
711         EventRecord event;
712         
713         [self getCarbonEvent:&event withEvent:theEvent];
714         event.what = keyUp;
715         
716         if (event.message == 0) {
717             event.message = [self keyMessageForEvent:theEvent];
718         }
719         
720         [self sendEvent:&event];
721     }
722 }
723
724 - (void)keyDown:(NSEvent *)theEvent
725 {
726     suspendKeyUpEvents = YES;
727     WKSendKeyEventToTSM(theEvent);
728 }
729
730 static OSStatus TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *pluginView)
731 {    
732     EventRef rawKeyEventRef;
733     OSStatus status = GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(EventRef), NULL, &rawKeyEventRef);
734     if (status != noErr) {
735         ERROR("GetEventParameter failed with error: %d", status);
736         return noErr;
737     }
738     
739     // Two-pass read to allocate/extract Mac charCodes
740     UInt32 numBytes;    
741     status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL);
742     if (status != noErr) {
743         ERROR("GetEventParameter failed with error: %d", status);
744         return noErr;
745     }
746     char *buffer = malloc(numBytes);
747     status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, numBytes, NULL, buffer);
748     if (status != noErr) {
749         ERROR("GetEventParameter failed with error: %d", status);
750         free(buffer);
751         return noErr;
752     }
753     
754     EventRef cloneEvent = CopyEvent(rawKeyEventRef);
755     unsigned i;
756     for (i = 0; i < numBytes; i++) {
757         status = SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes, typeChar, 1 /* one char code */, &buffer[i]);
758         if (status != noErr) {
759             ERROR("SetEventParameter failed with error: %d", status);
760             free(buffer);
761             return noErr;
762         }
763         
764         EventRecord eventRec;
765         if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
766             BOOL acceptedEvent;
767             acceptedEvent = [(WebBaseNetscapePluginView *)pluginView sendEvent:&eventRec];
768             
769             LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu",
770                 acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask));
771             
772             // We originally thought that if the plug-in didn't accept this event,
773             // we should pass it along so that keyboard scrolling, for example, will work.
774             // In practice, this is not a good idea, because plug-ins tend to eat the event but return false.
775             // MacIE handles each key event twice because of this, but we will emulate the other browsers instead.
776         }
777     }
778     ReleaseEvent(cloneEvent);
779     
780     free(buffer);
781     return noErr;
782 }
783
784 // Fake up command-modified events so cut, copy, paste and select all menus work.
785 - (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character
786 {
787     EventRecord event;
788     [self getCarbonEvent:&event];
789     event.what = keyDown;
790     event.modifiers |= cmdKey;
791     event.message = keyCode << 8 | character;
792     [self sendEvent:&event];
793 }
794
795 - (void)cut:(id)sender
796 {
797     [self sendModifierEventWithKeyCode:7 character:'x'];
798 }
799
800 - (void)copy:(id)sender
801 {
802     [self sendModifierEventWithKeyCode:8 character:'c'];
803 }
804
805 - (void)paste:(id)sender
806 {
807     [self sendModifierEventWithKeyCode:9 character:'v'];
808 }
809
810 - (void)selectAll:(id)sender
811 {
812     [self sendModifierEventWithKeyCode:0 character:'a'];
813 }
814
815 #pragma mark WEB_NETSCAPE_PLUGIN
816
817 - (BOOL)isNewWindowEqualToOldWindow
818 {
819     if (window.x != lastSetWindow.x) {
820         return NO;
821     }
822     if (window.y != lastSetWindow.y) {
823         return NO;
824     }
825     if (window.width != lastSetWindow.width) {
826         return NO;
827     }
828     if (window.height != lastSetWindow.height) {
829         return NO;
830     }
831     if (window.clipRect.top != lastSetWindow.clipRect.top) {
832         return NO;
833     }
834     if (window.clipRect.left != lastSetWindow.clipRect.left) {
835         return NO;
836     }
837     if (window.clipRect.bottom  != lastSetWindow.clipRect.bottom ) {
838         return NO;
839     }
840     if (window.clipRect.right != lastSetWindow.clipRect.right) {
841         return NO;
842     }
843     if (window.type != lastSetWindow.type) {
844         return NO;
845     }
846     if (nPort.portx != lastSetPort.portx) {
847         return NO;
848     }
849     if (nPort.porty != lastSetPort.porty) {
850         return NO;
851     }
852     if (nPort.port != lastSetPort.port) {
853         return NO;
854     }
855     
856     return YES;
857 }
858
859 - (void)updateAndSetWindow
860 {    
861     PortState portState = [self saveAndSetPortState];
862     [self setWindowIfNecessary];
863     [self restorePortState:portState];
864 }
865
866 - (void)setWindowIfNecessary
867 {
868     if (!isStarted) {
869         return;
870     }
871     
872     if (![self isNewWindowEqualToOldWindow]) {        
873         // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
874         // We probably don't want more general reentrancy protection; we are really
875         // protecting only against this one case, which actually comes up when
876         // you first install the SVG viewer plug-in.
877         NPError npErr;
878         ASSERT(!inSetWindow);
879         
880         inSetWindow = YES;
881         npErr = NPP_SetWindow(instance, &window);
882         inSetWindow = NO;
883
884         LOG(Plugins, "NPP_SetWindow: %d, port=0x%08x, window.x:%d window.y:%d",
885             npErr, (int)nPort.port, (int)window.x, (int)window.y);
886         
887         lastSetWindow = window;
888         lastSetPort = nPort;
889     }
890 }
891
892 - (void)removeTrackingRect
893 {
894     if (trackingTag) {
895         [self removeTrackingRect:trackingTag];
896         trackingTag = 0;
897
898         // Must release the window to balance the retain in resetTrackingRect.
899         // But must do it after setting trackingTag to 0 so we don't re-enter.
900         [[self window] release];
901     }
902 }
903
904 - (void)resetTrackingRect
905 {
906     [self removeTrackingRect];
907     if (isStarted) {
908         // Must retain the window so that removeTrackingRect can work after the window is closed.
909         [[self window] retain];
910         trackingTag = [self addTrackingRect:[self bounds] owner:self userData:nil assumeInside:NO];
911     }
912 }
913
914 + (void)setCurrentPluginView:(WebBaseNetscapePluginView *)view
915 {
916     currentPluginView = view;
917 }
918
919 + (WebBaseNetscapePluginView *)currentPluginView
920 {
921     return currentPluginView;
922 }
923
924 - (BOOL)canStart
925 {
926     return YES;
927 }
928
929 - (void)didStart
930 {
931     // Do nothing. Overridden by subclasses.
932 }
933
934 - (void)addWindowObservers
935 {
936     ASSERT([self window]);
937
938     NSWindow *theWindow = [self window];
939     
940     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
941     NSView *view;
942     for (view = self; view; view = [view superview]) {
943         [notificationCenter addObserver:self selector:@selector(viewHasMoved:)
944                                    name:NSViewFrameDidChangeNotification object:view];
945         [notificationCenter addObserver:self selector:@selector(viewHasMoved:)
946                                    name:NSViewBoundsDidChangeNotification object:view];
947     }
948     [notificationCenter addObserver:self selector:@selector(windowWillClose:)
949                                name:NSWindowWillCloseNotification object:theWindow];
950     [notificationCenter addObserver:self selector:@selector(windowBecameKey:)
951                                name:NSWindowDidBecomeKeyNotification object:theWindow];
952     [notificationCenter addObserver:self selector:@selector(windowResignedKey:)
953                                name:NSWindowDidResignKeyNotification object:theWindow];
954     [notificationCenter addObserver:self selector:@selector(windowDidMiniaturize:)
955                                name:NSWindowDidMiniaturizeNotification object:theWindow];
956     [notificationCenter addObserver:self selector:@selector(windowDidDeminiaturize:)
957                                name:NSWindowDidDeminiaturizeNotification object:theWindow];
958     
959     [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchFromUser:)
960                                name:LoginWindowDidSwitchFromUserNotification object:nil];
961     [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchToUser:)
962                                name:LoginWindowDidSwitchToUserNotification object:nil];
963 }
964
965 - (void)removeWindowObservers
966 {
967     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
968     [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification     object:nil];
969     [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification    object:nil];
970     [notificationCenter removeObserver:self name:NSWindowWillCloseNotification        object:nil];
971     [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification     object:nil];
972     [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification     object:nil];
973     [notificationCenter removeObserver:self name:NSWindowDidMiniaturizeNotification   object:nil];
974     [notificationCenter removeObserver:self name:NSWindowDidDeminiaturizeNotification object:nil];
975     [notificationCenter removeObserver:self name:LoginWindowDidSwitchFromUserNotification   object:nil];
976     [notificationCenter removeObserver:self name:LoginWindowDidSwitchToUserNotification     object:nil];
977 }
978
979 - (BOOL)start
980 {
981     ASSERT([self currentWindow]);
982     
983     if (isStarted) {
984         return YES;
985     }
986
987     if (![self canStart]) {
988         return NO;
989     }
990     
991     ASSERT([self webView]);
992     
993     if (![[[self webView] preferences] arePlugInsEnabled]) {
994         return NO;
995     }
996
997     ASSERT(NPP_New);
998
999     [[self class] setCurrentPluginView:self];
1000     NPError npErr = NPP_New((char *)[MIMEType cString], instance, mode, argsCount, cAttributes, cValues, NULL);
1001     [[self class] setCurrentPluginView:nil];
1002     
1003     LOG(Plugins, "NPP_New: %d", npErr);
1004     if (npErr != NPERR_NO_ERROR) {
1005         ERROR("NPP_New failed with error: %d", npErr);
1006         return NO;
1007     }
1008
1009     isStarted = YES;
1010         
1011     [self updateAndSetWindow];
1012
1013     if ([self window]) {
1014         [self addWindowObservers];
1015         if ([[self window] isKeyWindow]) {
1016             [self sendActivateEvent:YES];
1017         }
1018         [self restartNullEvents];
1019     }
1020
1021     [self resetTrackingRect];
1022     
1023     [self didStart];
1024     
1025     return YES;
1026 }
1027
1028 - (void)stop
1029 {
1030     [self removeTrackingRect];
1031
1032     if (!isStarted) {
1033         return;
1034     }
1035     
1036     isStarted = NO;
1037     
1038     // Stop any active streams
1039     [streams makeObjectsPerformSelector:@selector(stop)];
1040     
1041     // Stop the null events
1042     [self stopNullEvents];
1043
1044     // Set cursor back to arrow cursor
1045     [[NSCursor arrowCursor] set];
1046     
1047     // Stop notifications and callbacks.
1048     [self removeWindowObservers];
1049     [[pendingFrameLoads allKeys] makeObjectsPerformSelector:@selector(_setInternalLoadDelegate:) withObject:nil];
1050     [NSObject cancelPreviousPerformRequestsWithTarget:self];
1051
1052     // Setting the window type to 0 ensures that NPP_SetWindow will be called if the plug-in is restarted.
1053     lastSetWindow.type = 0;
1054     
1055     NPError npErr;
1056     npErr = NPP_Destroy(instance, NULL);
1057     LOG(Plugins, "NPP_Destroy: %d", npErr);
1058
1059     instance->pdata = NULL;
1060     
1061     // We usually remove the key event handler in resignFirstResponder but it is possible that resignFirstResponder 
1062     // may never get called so we can't completely rely on it.
1063     [self removeKeyEventHandler];
1064 }
1065
1066 - (BOOL)isStarted
1067 {
1068     return isStarted;
1069 }
1070
1071 - (WebDataSource *)dataSource
1072 {
1073     // Do nothing. Overridden by subclasses.
1074     return nil;
1075 }
1076
1077 - (WebFrame *)webFrame
1078 {
1079     return [[self dataSource] webFrame];
1080 }
1081
1082 - (WebView *)webView
1083 {
1084     return [[self webFrame] webView];
1085 }
1086
1087 - (NSWindow *)currentWindow
1088 {
1089     return [self window] ? [self window] : [[self webView] hostWindow];
1090 }
1091
1092 - (NPP)pluginPointer
1093 {
1094     return instance;
1095 }
1096
1097 - (WebNetscapePluginPackage *)plugin
1098 {
1099     return plugin;
1100 }
1101
1102 - (void)setPlugin:(WebNetscapePluginPackage *)thePlugin;
1103 {
1104     [thePlugin retain];
1105     [plugin release];
1106     plugin = thePlugin;
1107
1108     NPP_New =           [plugin NPP_New];
1109     NPP_Destroy =       [plugin NPP_Destroy];
1110     NPP_SetWindow =     [plugin NPP_SetWindow];
1111     NPP_NewStream =     [plugin NPP_NewStream];
1112     NPP_WriteReady =    [plugin NPP_WriteReady];
1113     NPP_Write =         [plugin NPP_Write];
1114     NPP_StreamAsFile =  [plugin NPP_StreamAsFile];
1115     NPP_DestroyStream = [plugin NPP_DestroyStream];
1116     NPP_HandleEvent =   [plugin NPP_HandleEvent];
1117     NPP_URLNotify =     [plugin NPP_URLNotify];
1118     NPP_GetValue =      [plugin NPP_GetValue];
1119     NPP_SetValue =      [plugin NPP_SetValue];
1120     NPP_Print =         [plugin NPP_Print];
1121 }
1122
1123 - (void)setMIMEType:(NSString *)theMIMEType
1124 {
1125     NSString *type = [theMIMEType copy];
1126     [MIMEType release];
1127     MIMEType = type;
1128 }
1129
1130 - (void)setBaseURL:(NSURL *)theBaseURL
1131 {
1132     [theBaseURL retain];
1133     [baseURL release];
1134     baseURL = theBaseURL;
1135 }
1136
1137 - (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values;
1138 {
1139     ASSERT([keys count] == [values count]);
1140     
1141     // Convert the attributes to 2 C string arrays.
1142     // These arrays are passed to NPP_New, but the strings need to be
1143     // modifiable and live the entire life of the plugin.
1144
1145     // The Java plug-in requires the first argument to be the base URL
1146     if ([MIMEType isEqualToString:@"application/x-java-applet"]) {
1147         cAttributes = (char **)malloc(([keys count] + 1) * sizeof(char *));
1148         cValues = (char **)malloc(([values count] + 1) * sizeof(char *));
1149         cAttributes[0] = strdup("DOCBASE");
1150         cValues[0] = strdup([baseURL _web_URLCString]);
1151         argsCount++;
1152     } else {
1153         cAttributes = (char **)malloc([keys count] * sizeof(char *));
1154         cValues = (char **)malloc([values count] * sizeof(char *));
1155     }
1156
1157     BOOL isWMP = [[[plugin bundle] bundleIdentifier] isEqualToString:@"com.microsoft.WMP.defaultplugin"];
1158     
1159     unsigned i;
1160     unsigned count = [keys count];
1161     for (i = 0; i < count; i++) {
1162         NSString *key = [keys objectAtIndex:i];
1163         NSString *value = [values objectAtIndex:i];
1164         if ([key _webkit_isCaseInsensitiveEqualToString:@"height"]) {
1165             specifiedHeight = [value intValue];
1166         } else if ([key _webkit_isCaseInsensitiveEqualToString:@"width"]) {
1167             specifiedWidth = [value intValue];
1168         }
1169         // Avoid Window Media Player crash when these attributes are present.
1170         if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) {
1171             continue;
1172         }
1173         cAttributes[argsCount] = strdup([key UTF8String]);
1174         cValues[argsCount] = strdup([value UTF8String]);
1175         LOG(Plugins, "%@ = %@", key, value);
1176         argsCount++;
1177     }
1178 }
1179
1180 - (void)setMode:(int)theMode
1181 {
1182     mode = theMode;
1183 }
1184
1185 #pragma mark NSVIEW
1186
1187 - initWithFrame:(NSRect)frame
1188 {
1189     [super initWithFrame:frame];
1190
1191     instance = &instanceStruct;
1192     instance->ndata = self;
1193     streams = [[NSMutableArray alloc] init];
1194     pendingFrameLoads = [[NSMutableDictionary alloc] init];
1195
1196     [[NSNotificationCenter defaultCenter] addObserver:self
1197                                              selector:@selector(preferencesHaveChanged:)
1198                                                  name:WebPreferencesChangedNotification
1199                                                object:nil];
1200
1201     return self;
1202 }
1203
1204 - (void)freeAttributeKeysAndValues
1205 {
1206     unsigned i;
1207     for (i = 0; i < argsCount; i++) {
1208         free(cAttributes[i]);
1209         free(cValues[i]);
1210     }
1211     free(cAttributes);
1212     free(cValues);
1213 }
1214
1215 - (void)dealloc
1216 {
1217     [[NSNotificationCenter defaultCenter] removeObserver:self];
1218     
1219     ASSERT(!isStarted);
1220
1221     [plugin release];
1222     [streams release];
1223     [MIMEType release];
1224     [baseURL release];
1225     [pendingFrameLoads release];
1226
1227     [self freeAttributeKeysAndValues];
1228
1229     [super dealloc];
1230 }
1231
1232 - (void)finalize
1233 {
1234     [[NSNotificationCenter defaultCenter] removeObserver:self];
1235
1236     ASSERT(!isStarted);
1237
1238     [self freeAttributeKeysAndValues];
1239
1240     [super finalize];
1241 }
1242
1243 - (void)drawRect:(NSRect)rect
1244 {
1245     if (!isStarted) {
1246         return;
1247     }
1248     
1249     if ([NSGraphicsContext currentContextDrawingToScreen]) {
1250         [self sendUpdateEvent];
1251     } else {
1252         // Printing 2862383
1253     }
1254 }
1255
1256 - (BOOL)isFlipped
1257 {
1258     return YES;
1259 }
1260
1261 -(void)tellQuickTimeToChill
1262 {
1263     // Make a call to the secret QuickDraw API that makes QuickTime calm down.
1264     WindowRef windowRef = [[self window] windowRef];
1265     if (!windowRef) {
1266         return;
1267     }
1268     CGrafPtr port = GetWindowPort(windowRef);
1269     Rect bounds;
1270     GetPortBounds(port, &bounds);
1271         WKCallDrawingNotification(port, &bounds);
1272 }
1273
1274 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
1275 {
1276     [self tellQuickTimeToChill];
1277
1278     // We must remove the tracking rect before we move to the new window.
1279     // Once we move to the new window, it will be too late.
1280     [self removeTrackingRect];
1281     [self removeWindowObservers];
1282     
1283     // Workaround for: <rdar://problem/3822871> resignFirstResponder is not sent to first responder view when it is removed from the window
1284     [self setHasFocus:NO];
1285
1286     if (!newWindow) {
1287         if ([[self webView] hostWindow]) {
1288             // View will be moved out of the actual window but it still has a host window.
1289             [self stopNullEvents];
1290         } else {
1291             // View will have no associated windows.
1292             [self stop];
1293         }
1294     }
1295 }
1296
1297 - (void)viewDidMoveToWindow
1298 {
1299     [self resetTrackingRect];
1300     
1301     if ([self window]) {
1302         // View moved to an actual window. Start it if not already started.
1303         [self start];
1304         [self restartNullEvents];
1305         [self addWindowObservers];
1306     } else if ([[self webView] hostWindow]) {
1307         // View moved out of an actual window, but still has a host window.
1308         // Call setWindow to explicitly "clip out" the plug-in from sight.
1309         // FIXME: It would be nice to do this where we call stopNullEvents in viewWillMoveToWindow.
1310         [self updateAndSetWindow];
1311     }
1312 }
1313
1314 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
1315 {
1316     if (!hostWindow && ![self window]) {
1317         // View will have no associated windows.
1318         [self stop];
1319     }
1320 }
1321
1322 - (void)viewDidMoveToHostWindow
1323 {
1324     if ([[self webView] hostWindow]) {
1325         // View now has an associated window. Start it if not already started.
1326         [self start];
1327     }
1328 }
1329
1330 #pragma mark NOTIFICATIONS
1331
1332 - (void)viewHasMoved:(NSNotification *)notification
1333 {
1334     [self tellQuickTimeToChill];
1335     [self updateAndSetWindow];
1336     [self resetTrackingRect];
1337     
1338     // Check to see if the plugin view is completely obscured (scrolled out of view, for example).
1339     // For performance reasons, we send null events at a lower rate to plugins which are obscured.
1340     BOOL oldIsObscured = isCompletelyObscured;
1341     isCompletelyObscured = NSEqualSizes([self visibleRect].size, NSZeroSize);
1342     if (isCompletelyObscured != oldIsObscured)
1343         [self restartNullEvents];
1344 }
1345
1346 - (void)windowWillClose:(NSNotification *)notification
1347 {
1348     [self stop];
1349 }
1350
1351 - (void)windowBecameKey:(NSNotification *)notification
1352 {
1353     [self sendActivateEvent:YES];
1354     [self setNeedsDisplay:YES];
1355     [self restartNullEvents];
1356     SetUserFocusWindow([[self window] windowRef]);
1357 }
1358
1359 - (void)windowResignedKey:(NSNotification *)notification
1360 {
1361     [self sendActivateEvent:NO];
1362     [self setNeedsDisplay:YES];
1363     [self restartNullEvents];
1364 }
1365
1366 - (void)windowDidMiniaturize:(NSNotification *)notification
1367 {
1368     [self stopNullEvents];
1369 }
1370
1371 - (void)windowDidDeminiaturize:(NSNotification *)notification
1372 {
1373     [self restartNullEvents];
1374 }
1375
1376 - (void)loginWindowDidSwitchFromUser:(NSNotification *)notification
1377 {
1378     [self stopNullEvents];
1379 }
1380
1381 -(void)loginWindowDidSwitchToUser:(NSNotification *)notification
1382 {
1383     [self restartNullEvents];
1384 }
1385
1386 - (void)preferencesHaveChanged:(NSNotification *)notification
1387 {
1388     WebPreferences *preferences = [[self webView] preferences];
1389     BOOL arePlugInsEnabled = [preferences arePlugInsEnabled];
1390     
1391     if ([notification object] == preferences && isStarted != arePlugInsEnabled) {
1392         if (arePlugInsEnabled) {
1393             if ([self currentWindow]) {
1394                 [self start];
1395             }
1396         } else {
1397             [self stop];
1398             [self setNeedsDisplay:YES];
1399         }
1400     }
1401 }
1402
1403 - (void *)pluginScriptableObject
1404 {
1405     if (NPP_GetValue) {
1406         void *value = 0;
1407         NPError npErr = NPP_GetValue (instance, NPPVpluginScriptableNPObject, (void *)&value);
1408         if (npErr == NPERR_NO_ERROR) {
1409             return value;
1410         }
1411     }
1412     return (void *)0;
1413 }
1414
1415
1416 @end
1417
1418 @implementation WebBaseNetscapePluginView (WebNPPCallbacks)
1419
1420 - (NSMutableURLRequest *)requestWithURLCString:(const char *)URLCString
1421 {
1422     if (!URLCString)
1423         return nil;
1424     
1425     CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, URLCString, kCFStringEncodingISOLatin1);
1426     ASSERT(string); // All strings should be representable in ISO Latin 1
1427     
1428     NSString *URLString = [(NSString *)string _web_stringByStrippingReturnCharacters];
1429     NSURL *URL = [NSURL _web_URLWithDataAsString:URLString relativeToURL:baseURL];
1430     CFRelease(string);
1431     if (!URL)
1432         return nil;
1433
1434     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
1435     [request _web_setHTTPReferrer:[[[self webFrame] _bridge] referrer]];
1436     return request;
1437 }
1438
1439 - (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest
1440 {
1441     // FIXME: Is this isStarted check needed here? evaluateJavaScriptPluginRequest should not be called
1442     // if we are stopped since this method is called after a delay and we call 
1443     // cancelPreviousPerformRequestsWithTarget inside of stop.
1444     if (!isStarted) {
1445         return;
1446     }
1447     
1448     NSURL *URL = [[JSPluginRequest request] URL];
1449     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1450     ASSERT(JSString);
1451     
1452     NSString *result = [[[self webFrame] _bridge] stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]];
1453     
1454     // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop.
1455     if (!isStarted) {
1456         return;
1457     }
1458         
1459     if ([JSPluginRequest frameName] != nil) {
1460         // FIXME: If the result is a string, we probably want to put that string into the frame, just
1461         // like we do in KHTMLPartBrowserExtension::openURLRequest.
1462         if ([JSPluginRequest sendNotification]) {
1463             NPP_URLNotify(instance, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]);
1464         }
1465     } else if ([result length] > 0) {
1466         // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does.
1467         NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding];
1468         WebBaseNetscapePluginStream *stream = [[WebBaseNetscapePluginStream alloc] initWithRequestURL:URL
1469                                                                                         pluginPointer:instance
1470                                                                                            notifyData:[JSPluginRequest notifyData]
1471                                                                                      sendNotification:[JSPluginRequest sendNotification]];
1472         [stream startStreamResponseURL:URL
1473                  expectedContentLength:[JSData length]
1474                       lastModifiedDate:nil
1475                               MIMEType:@"text/plain"];
1476         [stream receivedData:JSData];
1477         [stream finishedLoadingWithData:JSData];
1478         [stream release];
1479     }
1480 }
1481
1482 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
1483 {
1484     ASSERT(isStarted);
1485     
1486     WebPluginRequest *pluginRequest = [pendingFrameLoads objectForKey:webFrame];
1487     ASSERT(pluginRequest != nil);
1488     ASSERT([pluginRequest sendNotification]);
1489         
1490     NPP_URLNotify(instance, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]);
1491     
1492     [pendingFrameLoads removeObjectForKey:webFrame];
1493     [webFrame _setInternalLoadDelegate:nil];
1494 }
1495
1496 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
1497 {
1498     NPReason reason = NPRES_DONE;
1499     if (error != nil) {
1500         reason = [WebBaseNetscapePluginStream reasonForError:error];
1501     }    
1502     [self webFrame:webFrame didFinishLoadWithReason:reason];
1503 }
1504
1505 - (void)loadPluginRequest:(WebPluginRequest *)pluginRequest
1506 {
1507     NSURLRequest *request = [pluginRequest request];
1508     NSString *frameName = [pluginRequest frameName];
1509     WebFrame *frame = nil;
1510     
1511     NSURL *URL = [request URL];
1512     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1513     
1514     ASSERT(frameName || JSString);
1515     
1516     if (frameName) {
1517         // FIXME - need to get rid of this window creation which
1518         // bypasses normal targeted link handling
1519         frame = [[self webFrame] findFrameNamed:frameName];
1520     
1521         if (frame == nil) {
1522             WebView *newWebView = nil;
1523             WebView *currentWebView = [self webView];
1524             id wd = [currentWebView UIDelegate];
1525             if ([wd respondsToSelector:@selector(webView:createWebViewWithRequest:)]) {
1526                 newWebView = [wd webView:currentWebView createWebViewWithRequest:nil];
1527             } else {
1528                 newWebView = [[WebDefaultUIDelegate sharedUIDelegate] webView:currentWebView createWebViewWithRequest:nil];
1529             }
1530             frame = [newWebView mainFrame];
1531             [[frame _bridge] setName:frameName];
1532             [[newWebView _UIDelegateForwarder] webViewShow:newWebView];
1533         }
1534     }
1535
1536     if (JSString) {
1537         ASSERT(frame == nil || [self webFrame] == frame);
1538         [self evaluateJavaScriptPluginRequest:pluginRequest];
1539     } else {
1540         [frame loadRequest:request];
1541         if ([pluginRequest sendNotification]) {
1542             // Check if another plug-in view or even this view is waiting for the frame to load.
1543             // If it is, tell it that the load was cancelled because it will be anyway.
1544             WebBaseNetscapePluginView *view = [frame _internalLoadDelegate];
1545             if (view != nil) {
1546                 ASSERT([view isKindOfClass:[WebBaseNetscapePluginView class]]);
1547                 [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK];
1548             }
1549             [pendingFrameLoads _webkit_setObject:pluginRequest forUncopiedKey:frame];
1550             [frame _setInternalLoadDelegate:self];
1551         }
1552     }
1553 }
1554
1555 - (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification
1556 {
1557     NSURL *URL = [request URL];
1558
1559     if (!URL) {
1560         return NPERR_INVALID_URL;
1561     }
1562     
1563     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1564     if (JSString != nil) {
1565         if (![[[self webView] preferences] isJavaScriptEnabled]) {
1566             // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does.
1567             return NPERR_GENERIC_ERROR;
1568         } else if (cTarget == NULL && mode == NP_FULL) {
1569             // Don't allow a JavaScript request from a standalone plug-in that is self-targetted
1570             // because this can cause the user to be redirected to a blank page (3424039).
1571             return NPERR_INVALID_PARAM;
1572         }
1573     }
1574         
1575     if (cTarget || JSString) {
1576         // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't
1577         // want to potentially kill the plug-in inside of its URL request.
1578         NSString *target = nil;
1579         if (cTarget) {
1580             // Find the frame given the target string.
1581             target = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, cTarget, kCFStringEncodingWindowsLatin1);
1582         }
1583         
1584         WebFrame *frame = [self webFrame];
1585         if (JSString != nil && target != nil && [frame findFrameNamed:target] != frame) {
1586             // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
1587             CFRelease(target);
1588             return NPERR_INVALID_PARAM;
1589         }
1590         
1591         WebPluginRequest *pluginRequest = [[WebPluginRequest alloc] initWithRequest:request frameName:target notifyData:notifyData sendNotification:sendNotification didStartFromUserGesture:currentEventIsUserGesture];
1592         [self performSelector:@selector(loadPluginRequest:) withObject:pluginRequest afterDelay:0];
1593         [pluginRequest release];
1594         if (target) {
1595             CFRelease(target);
1596         }
1597     } else {
1598         WebNetscapePluginStream *stream = [[WebNetscapePluginStream alloc] initWithRequest:request 
1599                                                                              pluginPointer:instance 
1600                                                                                 notifyData:notifyData 
1601                                                                           sendNotification:sendNotification];
1602         if (!stream) {
1603             return NPERR_INVALID_URL;
1604         }
1605         [streams addObject:stream];
1606         [stream start];
1607         [stream release];
1608     }
1609     
1610     return NPERR_NO_ERROR;
1611 }
1612
1613 -(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData
1614 {
1615     LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget);
1616
1617     NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1618     return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES];
1619 }
1620
1621 -(NPError)getURL:(const char *)URLCString target:(const char *)cTarget
1622 {
1623     LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget);
1624
1625     NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1626     return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO];
1627 }
1628
1629 - (NPError)_postURL:(const char *)URLCString
1630              target:(const char *)target
1631                 len:(UInt32)len
1632                 buf:(const char *)buf
1633                file:(NPBool)file
1634          notifyData:(void *)notifyData
1635    sendNotification:(BOOL)sendNotification
1636        allowHeaders:(BOOL)allowHeaders
1637 {
1638     if (!URLCString || !len || !buf) {
1639         return NPERR_INVALID_PARAM;
1640     }
1641     
1642     NSData *postData = nil;
1643
1644     if (file) {
1645         // If we're posting a file, buf is either a file URL or a path to the file.
1646         NSString *bufString = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingWindowsLatin1);
1647         if (!bufString) {
1648             return NPERR_INVALID_PARAM;
1649         }
1650         NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString];
1651         NSString *path;
1652         if ([fileURL isFileURL]) {
1653             path = [fileURL path];
1654         } else {
1655             path = bufString;
1656         }
1657         postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]];
1658         CFRelease(bufString);
1659         if (!postData) {
1660             return NPERR_FILE_NOT_FOUND;
1661         }
1662     } else {
1663         postData = [NSData dataWithBytes:buf length:len];
1664     }
1665
1666     if ([postData length] == 0) {
1667         return NPERR_INVALID_PARAM;
1668     }
1669
1670     NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1671     [request setHTTPMethod:@"POST"];
1672     
1673     if (allowHeaders) {
1674         if ([postData _web_startsWithBlankLine]) {
1675             postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)];
1676         } else {
1677             unsigned location = [postData _web_locationAfterFirstBlankLine];
1678             if (location != NSNotFound) {
1679                 // If the blank line is somewhere in the middle of postData, everything before is the header.
1680                 NSData *headerData = [postData subdataWithRange:NSMakeRange(0, location)];
1681                 NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields];
1682                 unsigned dataLength = [postData length] - location;
1683
1684                 // Sometimes plugins like to set Content-Length themselves when they post,
1685                 // but WebFoundation does not like that. So we will remove the header
1686                 // and instead truncate the data to the requested length.
1687                 NSString *contentLength = [header objectForKey:@"Content-Length"];
1688
1689                 if (contentLength != nil) {
1690                     dataLength = MIN((unsigned)[contentLength intValue], dataLength);
1691                 }
1692                 [header removeObjectForKey:@"Content-Length"];
1693
1694                 if ([header count] > 0) {
1695                     [request setAllHTTPHeaderFields:header];
1696                 }
1697                 // Everything after the blank line is the actual content of the POST.
1698                 postData = [postData subdataWithRange:NSMakeRange(location, dataLength)];
1699
1700             }
1701         }
1702         if ([postData length] == 0) {
1703             return NPERR_INVALID_PARAM;
1704         }
1705     }
1706
1707     // Plug-ins expect to receive uncached data when doing a POST (3347134).
1708     [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
1709     [request setHTTPBody:postData];
1710     
1711     return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification];
1712 }
1713
1714 - (NPError)postURLNotify:(const char *)URLCString
1715                   target:(const char *)target
1716                      len:(UInt32)len
1717                      buf:(const char *)buf
1718                     file:(NPBool)file
1719               notifyData:(void *)notifyData
1720 {
1721     LOG(Plugins, "NPN_PostURLNotify: %s", URLCString);
1722     return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:notifyData sendNotification:YES allowHeaders:YES];
1723 }
1724
1725 -(NPError)postURL:(const char *)URLCString
1726            target:(const char *)target
1727               len:(UInt32)len
1728               buf:(const char *)buf
1729              file:(NPBool)file
1730 {
1731     LOG(Plugins, "NPN_PostURL: %s", URLCString);        
1732     // As documented, only allow headers to be specified via NPP_PostURL when using a file.
1733     return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:NULL sendNotification:NO allowHeaders:file];
1734 }
1735
1736 -(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream
1737 {
1738     LOG(Plugins, "NPN_NewStream");
1739     return NPERR_GENERIC_ERROR;
1740 }
1741
1742 -(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer
1743 {
1744     LOG(Plugins, "NPN_Write");
1745     return NPERR_GENERIC_ERROR;
1746 }
1747
1748 -(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason
1749 {
1750     LOG(Plugins, "NPN_DestroyStream");
1751     if (!stream->ndata) {
1752         return NPERR_INVALID_INSTANCE_ERROR;
1753     }
1754     WebBaseNetscapePluginStream *browserStream = (WebBaseNetscapePluginStream *)stream->ndata;
1755     [browserStream cancelLoadAndDestroyStreamWithError:[browserStream errorForReason:reason]];
1756     return NPERR_NO_ERROR;
1757 }
1758
1759 - (const char *)userAgent
1760 {
1761     return [[[self webView] userAgentForURL:baseURL] lossyCString];
1762 }
1763
1764 -(void)status:(const char *)message
1765 {    
1766     if (!message) {
1767         ERROR("NPN_Status passed a NULL status message");
1768         return;
1769     }
1770
1771     CFStringRef status = CFStringCreateWithCString(NULL, message, kCFStringEncodingWindowsLatin1);
1772     LOG(Plugins, "NPN_Status: %@", status);
1773     WebView *wv = [self webView];
1774     [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status];
1775     CFRelease(status);
1776 }
1777
1778 -(void)invalidateRect:(NPRect *)invalidRect
1779 {
1780     LOG(Plugins, "NPN_InvalidateRect");
1781     [self setNeedsDisplayInRect:NSMakeRect(invalidRect->left, invalidRect->top,
1782         (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)];
1783 }
1784
1785 -(void)invalidateRegion:(NPRegion)invalidRegion
1786 {
1787     LOG(Plugins, "NPN_InvalidateRegion");
1788     Rect invalidRect;
1789     GetRegionBounds(invalidRegion, &invalidRect);
1790     [self setNeedsDisplayInRect:NSMakeRect(invalidRect.left, invalidRect.top,
1791         (float)invalidRect.right - invalidRect.left, (float)invalidRect.bottom - invalidRect.top)];
1792 }
1793
1794 -(void)forceRedraw
1795 {
1796     LOG(Plugins, "forceRedraw");
1797     [self setNeedsDisplay:YES];
1798     [[self window] displayIfNeeded];
1799 }
1800
1801 - (NPError)getVariable:(NPNVariable)variable value:(void *)value
1802 {
1803     if (variable == NPNVWindowNPObject) {
1804         NPObject *windowScriptObject = [[[self webFrame] _bridge] windowScriptNPObject];
1805
1806         // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
1807         if (windowScriptObject)
1808             _NPN_RetainObject(windowScriptObject);
1809         
1810         void **v = (void **)value;
1811         *v = windowScriptObject;
1812         
1813         return NPERR_NO_ERROR;
1814     }
1815     return NPERR_GENERIC_ERROR;
1816 }
1817
1818 - (NPError)setVariable:(NPPVariable)variable value:(void *)value
1819 {
1820     switch (variable) {
1821         case NPPVpluginTransparentBool:
1822         {
1823             BOOL newTransparent = (value != 0);
1824             
1825             // Redisplay if transparency is changing
1826             if (isTransparent != newTransparent)
1827                 [self setNeedsDisplay:YES];
1828             
1829             isTransparent = newTransparent;
1830             
1831             return NPERR_NO_ERROR;
1832         }
1833         
1834         default:
1835             return NPERR_GENERIC_ERROR;
1836     }
1837 }
1838
1839 @end
1840
1841 @implementation WebPluginRequest
1842
1843 - (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture
1844 {
1845     [super init];
1846     _didStartFromUserGesture = currentEventIsUserGesture;
1847     _request = [request retain];
1848     _frameName = [frameName retain];
1849     _notifyData = notifyData;
1850     _sendNotification = sendNotification;
1851     return self;
1852 }
1853
1854 - (void)dealloc
1855 {
1856     [_request release];
1857     [_frameName release];
1858     [super dealloc];
1859 }
1860
1861 - (NSURLRequest *)request
1862 {
1863     return _request;
1864 }
1865
1866 - (NSString *)frameName
1867 {
1868     return _frameName;
1869 }
1870
1871 - (BOOL)isCurrentEventUserGesture
1872 {
1873     return _didStartFromUserGesture;
1874 }
1875
1876 - (BOOL)sendNotification
1877 {
1878     return _sendNotification;
1879 }
1880
1881 - (void *)notifyData
1882 {
1883     return _notifyData;
1884 }
1885
1886 @end
1887
1888 @implementation NSData (PluginExtras)
1889
1890 - (BOOL)_web_startsWithBlankLine
1891 {
1892     return [self length] > 0 && ((const char *)[self bytes])[0] == '\n';
1893 }
1894
1895
1896 - (unsigned)_web_locationAfterFirstBlankLine
1897 {
1898     const char *bytes = (const char *)[self bytes];
1899     unsigned length = [self length];
1900     
1901     unsigned i;
1902     for (i = 0; i < length - 4; i++) {
1903         
1904         //  Support for Acrobat. It sends "\n\n".
1905         if (bytes[i] == '\n' && bytes[i+1] == '\n') {
1906             return i+2;
1907         }
1908         
1909         // Returns the position after 2 CRLF's or 1 CRLF if it is the first line.
1910         if (bytes[i] == '\r' && bytes[i+1] == '\n') {
1911             i += 2;
1912             if (i == 2) {
1913                 return i;
1914             } else if (bytes[i] == '\n') {
1915                 // Support for Director. It sends "\r\n\n" (3880387).
1916                 return i+1;
1917             } else if (bytes[i] == '\r' && bytes[i+1] == '\n') {
1918                 // Support for Flash. It sends "\r\n\r\n" (3758113).
1919                 return i+2;
1920             }
1921         }
1922     }
1923     return NSNotFound;
1924 }
1925
1926 @end