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