2 * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #import <WebKit/WebBaseNetscapePluginView.h>
31 #import <Accelerate/Accelerate.h>
32 #import <JavaScriptCore/Assertions.h>
33 #import <WebKit/DOMPrivate.h>
34 #import <WebKit/WebFrameBridge.h>
35 #import <WebKit/WebDataSource.h>
36 #import <WebKit/WebDefaultUIDelegate.h>
37 #import <WebKit/WebFrameInternal.h>
38 #import <WebKit/WebFrameLoader.h>
39 #import <WebKit/WebFrameView.h>
40 #import <WebKit/WebKitLogging.h>
41 #import <WebKit/WebKitNSStringExtras.h>
42 #import <WebKit/WebNetscapePluginStream.h>
43 #import <WebKit/WebNullPluginView.h>
44 #import <WebKit/WebNSDataExtras.h>
45 #import <WebKit/WebNSDictionaryExtras.h>
46 #import <WebKit/WebNSObjectExtras.h>
47 #import <WebKit/WebNSURLExtras.h>
48 #import <WebKit/WebNSURLRequestExtras.h>
49 #import <WebKit/WebNSViewExtras.h>
50 #import <WebKit/WebNetscapePluginPackage.h>
51 #import <WebKit/WebPreferences.h>
52 #import <WebKit/WebViewInternal.h>
54 #import <WebKit/WebUIDelegate.h>
55 #import <WebKitSystemInterface.h>
56 #import <JavaScriptCore/npruntime_impl.h>
58 #import <Carbon/Carbon.h>
60 #import <objc/objc-runtime.h>
62 // Send null events 50 times a second when active, so plug-ins like Flash get high frame rates.
63 #define NullEventIntervalActive 0.02
64 #define NullEventIntervalNotActive 0.25
66 #define LoginWindowDidSwitchFromUserNotification @"WebLoginWindowDidSwitchFromUserNotification"
67 #define LoginWindowDidSwitchToUserNotification @"WebLoginWindowDidSwitchToUserNotification"
69 @interface WebBaseNetscapePluginView (Internal)
70 - (void)_viewHasMoved;
71 - (NSBitmapImageRep *)_printedPluginBitmap;
72 - (BOOL)_createAGLContextIfNeeded;
73 - (BOOL)_createWindowedAGLContext;
74 - (BOOL)_createWindowlessAGLContext;
75 - (CGLContextObj)_cglContext;
76 - (BOOL)_getAGLOffscreenBuffer:(GLvoid **)outBuffer width:(GLsizei *)outWidth height:(GLsizei *)outHeight;
77 - (void)_destroyAGLContext;
78 - (void)_reshapeAGLWindow;
79 - (void)_hideAGLWindow;
80 - (NSImage *)_aglOffscreenImageForDrawingInRect:(NSRect)drawingInRect;
83 static WebBaseNetscapePluginView *currentPluginView = nil;
85 typedef struct OpaquePortState* PortState;
87 #ifndef NP_NO_QUICKDRAW
89 // QuickDraw is not available in 64-bit
94 RgnHandle oldClipRegion;
95 RgnHandle oldVisibleRegion;
100 #endif /* NP_NO_QUICKDRAW */
103 CGContextRef context;
107 AGLContext oldContext;
110 @interface WebPluginRequest : NSObject
112 NSURLRequest *_request;
113 NSString *_frameName;
115 BOOL _didStartFromUserGesture;
116 BOOL _sendNotification;
119 - (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture;
121 - (NSURLRequest *)request;
122 - (NSString *)frameName;
123 - (void *)notifyData;
124 - (BOOL)isCurrentEventUserGesture;
125 - (BOOL)sendNotification;
129 @interface NSData (WebPluginDataExtras)
130 - (BOOL)_web_startsWithBlankLine;
131 - (WebNSInteger)_web_locationAfterFirstBlankLine;
134 static OSStatus TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *pluginView);
136 @interface WebBaseNetscapePluginView (ForwardDeclarations)
137 - (void)setWindowIfNecessary;
140 @implementation WebBaseNetscapePluginView
144 WKSendUserChangeNotifications();
149 + (void)getCarbonEvent:(EventRecord *)carbonEvent
151 carbonEvent->what = nullEvent;
152 carbonEvent->message = 0;
153 carbonEvent->when = TickCount();
154 GetGlobalMouse(&carbonEvent->where);
155 carbonEvent->where.h *= HIGetScaleFactor();
156 carbonEvent->where.v *= HIGetScaleFactor();
157 carbonEvent->modifiers = GetCurrentKeyModifiers();
159 carbonEvent->modifiers |= btnState;
162 - (void)getCarbonEvent:(EventRecord *)carbonEvent
164 [[self class] getCarbonEvent:carbonEvent];
167 - (EventModifiers)modifiersForEvent:(NSEvent *)event
169 EventModifiers modifiers;
170 unsigned int modifierFlags = [event modifierFlags];
171 NSEventType eventType = [event type];
175 if (eventType != NSLeftMouseDown && eventType != NSRightMouseDown)
176 modifiers |= btnState;
178 if (modifierFlags & NSCommandKeyMask)
181 if (modifierFlags & NSShiftKeyMask)
182 modifiers |= shiftKey;
184 if (modifierFlags & NSAlphaShiftKeyMask)
185 modifiers |= alphaLock;
187 if (modifierFlags & NSAlternateKeyMask)
188 modifiers |= optionKey;
190 if (modifierFlags & NSControlKeyMask || eventType == NSRightMouseDown)
191 modifiers |= controlKey;
196 - (void)getCarbonEvent:(EventRecord *)carbonEvent withEvent:(NSEvent *)cocoaEvent
198 if (WKConvertNSEventToCarbonEvent(carbonEvent, cocoaEvent)) {
199 carbonEvent->where.h *= HIGetScaleFactor();
200 carbonEvent->where.v *= HIGetScaleFactor();
204 NSPoint where = [[cocoaEvent window] convertBaseToScreen:[cocoaEvent locationInWindow]];
206 carbonEvent->what = nullEvent;
207 carbonEvent->message = 0;
208 carbonEvent->when = (UInt32)([cocoaEvent timestamp] * 60); // seconds to ticks
209 carbonEvent->where.h = (short)where.x;
210 carbonEvent->where.v = (short)(NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - where.y);
211 carbonEvent->modifiers = [self modifiersForEvent:cocoaEvent];
214 - (BOOL)superviewsHaveSuperviews
216 NSView *contentView = [[self window] contentView];
218 for (view = self; view != nil; view = [view superview]) {
219 if (view == contentView) {
226 #ifndef NP_NO_QUICKDRAW
227 // The WindowRef created by -[NSWindow windowRef] has a QuickDraw GrafPort that covers
228 // the entire window frame (or structure region to use the Carbon term) rather then just the window content.
229 // We can remove this when <rdar://problem/4201099> is fixed.
230 - (void)fixWindowPort
232 ASSERT(drawingModel == NPDrawingModelQuickDraw);
234 NSWindow *currentWindow = [self currentWindow];
235 if ([currentWindow isKindOfClass:objc_getClass("NSCarbonWindow")])
238 float windowHeight = [currentWindow frame].size.height;
239 NSView *contentView = [currentWindow contentView];
240 NSRect contentRect = [contentView convertRect:[contentView frame] toView:nil]; // convert to window-relative coordinates
244 SetPort(GetWindowPort([currentWindow windowRef]));
246 MovePortTo(contentRect.origin.x, /* Flip Y */ windowHeight - NSMaxY(contentRect));
247 PortSize(contentRect.size.width, contentRect.size.height);
253 - (PortState)saveAndSetNewPortStateForUpdate:(BOOL)forUpdate
255 ASSERT([self currentWindow] != nil);
257 #ifndef NP_NO_QUICKDRAW
258 // If drawing with QuickDraw, fix the window port so that it has the same bounds as the NSWindow's
259 // content view. This makes it easier to convert between AppKit view and QuickDraw port coordinates.
260 if (drawingModel == NPDrawingModelQuickDraw)
261 [self fixWindowPort];
264 WindowRef windowRef = [[self currentWindow] windowRef];
267 // Use AppKit to convert view coordinates to NSWindow coordinates.
268 NSRect boundsInWindow = [self convertRect:[self bounds] toView:nil];
269 NSRect visibleRectInWindow = [self convertRect:[self visibleRect] toView:nil];
271 // Flip Y to convert NSWindow coordinates to top-left-based window coordinates.
272 float borderViewHeight = [[self currentWindow] frame].size.height;
273 boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow);
274 visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow);
276 #ifndef NP_NO_QUICKDRAW
277 // Look at the Carbon port to convert top-left-based window coordinates into top-left-based content coordinates.
278 if (drawingModel == NPDrawingModelQuickDraw) {
280 CGrafPtr port = GetWindowPort(windowRef);
281 GetPortBounds(port, &portBounds);
283 PixMap *pix = *GetPortPixMap(port);
284 boundsInWindow.origin.x += pix->bounds.left - portBounds.left;
285 boundsInWindow.origin.y += pix->bounds.top - portBounds.top;
286 visibleRectInWindow.origin.x += pix->bounds.left - portBounds.left;
287 visibleRectInWindow.origin.y += pix->bounds.top - portBounds.top;
291 window.x = (int32)boundsInWindow.origin.x;
292 window.y = (int32)boundsInWindow.origin.y;
293 window.width = NSWidth(boundsInWindow);
294 window.height = NSHeight(boundsInWindow);
296 // "Clip-out" the plug-in when:
297 // 1) it's not really in a window or off-screen or has no height or width.
298 // 2) window.x is a "big negative number" which is how WebCore expresses off-screen widgets.
299 // 3) the window is miniaturized or the app is hidden
300 // 4) we're inside of viewWillMoveToWindow: with a nil window. In this case, superviews may already have nil
301 // superviews and nil windows and results from convertRect:toView: are incorrect.
302 NSWindow *realWindow = [self window];
303 if (window.width <= 0 || window.height <= 0 || window.x < -100000
304 || realWindow == nil || [realWindow isMiniaturized]
306 || ![self superviewsHaveSuperviews]
307 || [self isHiddenOrHasHiddenAncestor]) {
308 // The following code tries to give plug-ins the same size they will eventually have.
309 // The specifiedWidth and specifiedHeight variables are used to predict the size that
310 // WebCore will eventually resize us to.
312 // The QuickTime plug-in has problems if you give it a width or height of 0.
313 // Since other plug-ins also might have the same sort of trouble, we make sure
314 // to always give plug-ins a size other than 0,0.
316 if (window.width <= 0) {
317 window.width = specifiedWidth > 0 ? specifiedWidth : 100;
319 if (window.height <= 0) {
320 window.height = specifiedHeight > 0 ? specifiedHeight : 100;
323 window.clipRect.bottom = window.clipRect.top;
324 window.clipRect.left = window.clipRect.right;
326 window.clipRect.top = (uint16)visibleRectInWindow.origin.y;
327 window.clipRect.left = (uint16)visibleRectInWindow.origin.x;
328 window.clipRect.bottom = (uint16)(visibleRectInWindow.origin.y + visibleRectInWindow.size.height);
329 window.clipRect.right = (uint16)(visibleRectInWindow.origin.x + visibleRectInWindow.size.width);
332 // Save the port state, set up the port for entry into the plugin
334 switch (drawingModel) {
335 #ifndef NP_NO_QUICKDRAW
336 case NPDrawingModelQuickDraw:
340 CGrafPtr port = GetWindowPort(windowRef);
341 GetPortBounds(port, &portBounds);
342 nPort.qdPort.port = port;
343 nPort.qdPort.portx = (int32)-boundsInWindow.origin.x;
344 nPort.qdPort.porty = (int32)-boundsInWindow.origin.y;
345 window.window = &nPort;
347 PortState_QD *qdPortState = malloc(sizeof(PortState_QD));
348 portState = (PortState)qdPortState;
350 GetPort(&qdPortState->oldPort);
352 qdPortState->oldOrigin.h = portBounds.left;
353 qdPortState->oldOrigin.v = portBounds.top;
355 qdPortState->oldClipRegion = NewRgn();
356 GetPortClipRegion(port, qdPortState->oldClipRegion);
358 qdPortState->oldVisibleRegion = NewRgn();
359 GetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
361 RgnHandle clipRegion = NewRgn();
362 qdPortState->clipRegion = clipRegion;
364 MacSetRectRgn(clipRegion,
365 window.clipRect.left + nPort.qdPort.portx, window.clipRect.top + nPort.qdPort.porty,
366 window.clipRect.right + nPort.qdPort.portx, window.clipRect.bottom + nPort.qdPort.porty);
368 // Clip to dirty region so plug-in does not draw over already-drawn regions of the window that are
369 // not going to be redrawn this update. This forces plug-ins to play nice with z-index ordering.
371 RgnHandle viewClipRegion = NewRgn();
373 // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
374 // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
375 // knows about the true set of dirty rects.
376 NSView *opaqueAncestor = [self opaqueAncestor];
377 const NSRect *dirtyRects;
378 int dirtyRectCount, dirtyRectIndex;
379 [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount];
381 for (dirtyRectIndex = 0; dirtyRectIndex < dirtyRectCount; dirtyRectIndex++) {
382 NSRect dirtyRect = [self convertRect:dirtyRects[dirtyRectIndex] fromView:opaqueAncestor];
383 if (!NSEqualSizes(dirtyRect.size, NSZeroSize)) {
384 // Create a region for this dirty rect
385 RgnHandle dirtyRectRegion = NewRgn();
386 SetRectRgn(dirtyRectRegion, NSMinX(dirtyRect), NSMinY(dirtyRect), NSMaxX(dirtyRect), NSMaxY(dirtyRect));
388 // Union this dirty rect with the rest of the dirty rects
389 UnionRgn(viewClipRegion, dirtyRectRegion, viewClipRegion);
390 DisposeRgn(dirtyRectRegion);
394 // Intersect the dirty region with the clip region, so that we only draw over dirty parts
395 SectRgn(clipRegion, viewClipRegion, clipRegion);
396 DisposeRgn(viewClipRegion);
399 qdPortState->forUpdate = forUpdate;
401 // Switch to the port and set it up.
405 ForeColor(blackColor);
406 BackColor(whiteColor);
408 SetOrigin(nPort.qdPort.portx, nPort.qdPort.porty);
410 SetPortClipRegion(nPort.qdPort.port, clipRegion);
413 // AppKit may have tried to help us by doing a BeginUpdate.
414 // But the invalid region at that level didn't include AppKit's notion of what was not valid.
415 // We reset the port's visible region to counteract what BeginUpdate did.
416 SetPortVisibleRegion(nPort.qdPort.port, clipRegion);
418 // Some plugins do their own BeginUpdate/EndUpdate.
419 // For those, we must make sure that the update region contains the area we want to draw.
420 InvalWindowRgn(windowRef, clipRegion);
424 #endif /* NP_NO_QUICKDRAW */
426 case NPDrawingModelCoreGraphics:
428 // A CoreGraphics plugin's window may only be set while the plugin view is being updated
429 ASSERT(forUpdate && [NSView focusView] == self);
431 PortState_CG *cgPortState = (PortState_CG *)malloc(sizeof(PortState_CG));
432 portState = (PortState)cgPortState;
433 cgPortState->context = [[NSGraphicsContext currentContext] graphicsPort];
435 // Update the plugin's window/context
436 nPort.cgPort.window = windowRef;
437 nPort.cgPort.context = cgPortState->context;
438 window.window = &nPort.cgPort;
440 // Save current graphics context's state; will be restored by -restorePortState:
441 CGContextSaveGState(nPort.cgPort.context);
443 // FIXME (4544971): Clip to dirty region when updating in "windowless" mode (transparent), like in the QD case
447 case NPDrawingModelOpenGL:
449 // An OpenGL plugin's window may only be set while the plugin view is being updated
450 ASSERT(forUpdate && [NSView focusView] == self);
452 // Clear the "current" window and context -- they will be assigned below (if all goes well)
453 nPort.aglPort.window = NULL;
454 nPort.aglPort.context = NULL;
456 // Create AGL context if needed
457 if (![self _createAGLContextIfNeeded]) {
458 LOG_ERROR("Could not create AGL context");
462 // Update the plugin's window/context
463 nPort.aglPort.window = windowRef;
464 nPort.aglPort.context = [self _cglContext];
465 window.window = &nPort.aglPort;
467 // Save/set current AGL context
468 PortState_GL *glPortState = (PortState_GL *)malloc(sizeof(PortState_GL));
469 portState = (PortState)glPortState;
470 glPortState->oldContext = aglGetCurrentContext();
471 aglSetCurrentContext(aglContext);
473 // Adjust viewport according to clip
474 switch (window.type) {
475 case NPWindowTypeWindow:
476 glViewport(NSMinX(boundsInWindow) - NSMinX(visibleRectInWindow), NSMaxY(visibleRectInWindow) - NSMaxY(boundsInWindow), window.width, window.height);
479 case NPWindowTypeDrawable:
481 GLsizei width, height;
482 if ([self _getAGLOffscreenBuffer:NULL width:&width height:&height])
483 glViewport(0, 0, width, height);
488 ASSERT_NOT_REACHED();
495 ASSERT_NOT_REACHED();
503 - (PortState)saveAndSetNewPortState
505 return [self saveAndSetNewPortStateForUpdate:NO];
508 - (void)restorePortState:(PortState)portState
510 ASSERT([self currentWindow]);
513 switch (drawingModel) {
514 #ifndef NP_NO_QUICKDRAW
515 case NPDrawingModelQuickDraw:
517 PortState_QD *qdPortState = (PortState_QD *)portState;
518 WindowRef windowRef = [[self currentWindow] windowRef];
519 CGrafPtr port = GetWindowPort(windowRef);
520 if (qdPortState->forUpdate)
521 ValidWindowRgn(windowRef, qdPortState->clipRegion);
523 SetOrigin(qdPortState->oldOrigin.h, qdPortState->oldOrigin.v);
525 SetPortClipRegion(port, qdPortState->oldClipRegion);
526 if (qdPortState->forUpdate)
527 SetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
529 DisposeRgn(qdPortState->oldClipRegion);
530 DisposeRgn(qdPortState->oldVisibleRegion);
531 DisposeRgn(qdPortState->clipRegion);
533 SetPort(qdPortState->oldPort);
536 #endif /* NP_NO_QUICKDRAW */
538 case NPDrawingModelCoreGraphics:
540 ASSERT([NSView focusView] == self);
541 ASSERT(((PortState_CG *)portState)->context == nPort.cgPort.context);
542 CGContextRestoreGState(nPort.cgPort.context);
546 case NPDrawingModelOpenGL:
547 aglSetCurrentContext(((PortState_GL *)portState)->oldContext);
551 ASSERT_NOT_REACHED();
556 - (BOOL)sendEvent:(EventRecord *)event
562 // If at any point the user clicks or presses a key from within a plugin, set the
563 // currentEventIsUserGesture flag to true. This is important to differentiate legitimate
564 // window.open() calls; we still want to allow those. See rdar://problem/4010765
565 if (event->what == mouseDown || event->what == keyDown || event->what == mouseUp || event->what == autoKey)
566 currentEventIsUserGesture = YES;
568 suspendKeyUpEvents = NO;
573 ASSERT(NPP_HandleEvent);
575 // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
576 // We probably don't want more general reentrancy protection; we are really
577 // protecting only against this one case, which actually comes up when
578 // you first install the SVG viewer plug-in.
582 BOOL defers = [[self webView] defersCallbacks];
584 [[self webView] setDefersCallbacks:YES];
586 // Can only send updateEvt to CoreGraphics and OpenGL plugins when actually drawing
587 ASSERT((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || event->what != updateEvt || [NSView focusView] == self);
589 BOOL updating = event->what == updateEvt;
591 if ((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || event->what == updateEvt) {
592 // In CoreGraphics or OpenGL mode, the port state only needs to be saved/set when redrawing the plug-in view. The plug-in is not
593 // allowed to draw at any other time.
594 portState = [self saveAndSetNewPortStateForUpdate:updating];
596 // We may have changed the window, so inform the plug-in.
597 [self setWindowIfNecessary];
601 #if !defined(NDEBUG) && !defined(NP_NO_QUICKDRAW)
602 // Draw green to help debug.
603 // If we see any green we know something's wrong.
604 // Note that PaintRect() only works for QuickDraw plugins; otherwise the current QD port is undefined.
605 if (drawingModel == NPDrawingModelQuickDraw && !isTransparent && event->what == updateEvt) {
606 ForeColor(greenColor);
607 const Rect bigRect = { -10000, -10000, 10000, 10000 };
609 ForeColor(blackColor);
613 // Temporarily retain self in case the plug-in view is released while sending an event.
614 [[self retain] autorelease];
616 [self willCallPlugInFunction];
617 BOOL acceptedEvent = NPP_HandleEvent(instance, event);
618 [self didCallPlugInFunction];
620 currentEventIsUserGesture = NO;
623 if ([self currentWindow])
624 [self restorePortState:portState];
629 [[self webView] setDefersCallbacks:NO];
631 return acceptedEvent;
634 - (void)sendActivateEvent:(BOOL)activate
638 [self getCarbonEvent:&event];
639 event.what = activateEvt;
640 WindowRef windowRef = [[self window] windowRef];
641 event.message = (unsigned long)windowRef;
643 event.modifiers |= activeFlag;
647 acceptedEvent = [self sendEvent:&event];
649 LOG(PluginEvents, "NPP_HandleEvent(activateEvent): %d isActive: %d", acceptedEvent, activate);
652 - (BOOL)sendUpdateEvent
656 [self getCarbonEvent:&event];
657 event.what = updateEvt;
658 WindowRef windowRef = [[self window] windowRef];
659 event.message = (unsigned long)windowRef;
661 BOOL acceptedEvent = [self sendEvent:&event];
663 LOG(PluginEvents, "NPP_HandleEvent(updateEvt): %d", acceptedEvent);
665 return acceptedEvent;
672 [self getCarbonEvent:&event];
674 // Plug-in should not react to cursor position when not active or when a menu is down.
675 MenuTrackingData trackingData;
676 OSStatus error = GetMenuTrackingData(NULL, &trackingData);
678 // Plug-in should not react to cursor position when the actual window is not key.
679 if (![[self window] isKeyWindow] || (error == noErr && trackingData.menu)) {
680 // FIXME: Does passing a v and h of -1 really prevent it from reacting to the cursor position?
685 [self sendEvent:&event];
688 - (void)stopNullEvents
690 [nullEventTimer invalidate];
691 [nullEventTimer release];
692 nullEventTimer = nil;
695 - (void)restartNullEvents
697 ASSERT([self window]);
700 [self stopNullEvents];
702 if (!isStarted || [[self window] isMiniaturized])
705 NSTimeInterval interval;
707 // If the plugin is completely obscured (scrolled out of view, for example), then we will
708 // send null events at a reduced rate.
709 interval = !isCompletelyObscured ? NullEventIntervalActive : NullEventIntervalNotActive;
710 nullEventTimer = [[NSTimer scheduledTimerWithTimeInterval:interval
712 selector:@selector(sendNullEvent)
714 repeats:YES] retain];
717 - (BOOL)acceptsFirstResponder
722 - (void)installKeyEventHandler
724 static const EventTypeSpec sTSMEvents[] =
726 { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
729 if (!keyEventHandler) {
730 InstallEventHandler(GetWindowEventTarget([[self window] windowRef]),
731 NewEventHandlerUPP(TSMEventHandler),
732 GetEventTypeCount(sTSMEvents),
739 - (void)removeKeyEventHandler
741 if (keyEventHandler) {
742 RemoveEventHandler(keyEventHandler);
743 keyEventHandler = NULL;
747 - (void)setHasFocus:(BOOL)flag
749 if (hasFocus != flag) {
752 [self getCarbonEvent:&event];
755 event.what = getFocusEvent;
756 acceptedEvent = [self sendEvent:&event];
757 LOG(PluginEvents, "NPP_HandleEvent(getFocusEvent): %d", acceptedEvent);
758 [self installKeyEventHandler];
760 event.what = loseFocusEvent;
761 acceptedEvent = [self sendEvent:&event];
762 LOG(PluginEvents, "NPP_HandleEvent(loseFocusEvent): %d", acceptedEvent);
763 [self removeKeyEventHandler];
768 - (BOOL)becomeFirstResponder
770 [self setHasFocus:YES];
774 - (BOOL)resignFirstResponder
776 [self setHasFocus:NO];
780 // AppKit doesn't call mouseDown or mouseUp on right-click. Simulate control-click
781 // mouseDown and mouseUp so plug-ins get the right-click event as they do in Carbon (3125743).
782 - (void)rightMouseDown:(NSEvent *)theEvent
784 [self mouseDown:theEvent];
787 - (void)rightMouseUp:(NSEvent *)theEvent
789 [self mouseUp:theEvent];
792 - (void)mouseDown:(NSEvent *)theEvent
796 [self getCarbonEvent:&event withEvent:theEvent];
797 event.what = mouseDown;
800 acceptedEvent = [self sendEvent:&event];
802 LOG(PluginEvents, "NPP_HandleEvent(mouseDown): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
805 - (void)mouseUp:(NSEvent *)theEvent
809 [self getCarbonEvent:&event withEvent:theEvent];
810 event.what = mouseUp;
813 acceptedEvent = [self sendEvent:&event];
815 LOG(PluginEvents, "NPP_HandleEvent(mouseUp): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
818 - (void)mouseEntered:(NSEvent *)theEvent
822 [self getCarbonEvent:&event withEvent:theEvent];
823 event.what = adjustCursorEvent;
826 acceptedEvent = [self sendEvent:&event];
828 LOG(PluginEvents, "NPP_HandleEvent(mouseEntered): %d", acceptedEvent);
831 - (void)mouseExited:(NSEvent *)theEvent
835 [self getCarbonEvent:&event withEvent:theEvent];
836 event.what = adjustCursorEvent;
839 acceptedEvent = [self sendEvent:&event];
841 LOG(PluginEvents, "NPP_HandleEvent(mouseExited): %d", acceptedEvent);
843 // Set cursor back to arrow cursor.
844 [[NSCursor arrowCursor] set];
847 - (void)mouseDragged:(NSEvent *)theEvent
849 // Do nothing so that other responders don't respond to the drag that initiated in this view.
852 - (UInt32)keyMessageForEvent:(NSEvent *)event
854 NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())];
859 [data getBytes:&characterCode length:1];
860 UInt16 keyCode = [event keyCode];
861 return keyCode << 8 | characterCode;
864 - (void)keyUp:(NSEvent *)theEvent
866 WKSendKeyEventToTSM(theEvent);
868 // TSM won't send keyUp events so we have to send them ourselves.
869 // Only send keyUp events after we receive the TSM callback because this is what plug-in expect from OS 9.
870 if (!suspendKeyUpEvents) {
873 [self getCarbonEvent:&event withEvent:theEvent];
876 if (event.message == 0) {
877 event.message = [self keyMessageForEvent:theEvent];
880 [self sendEvent:&event];
884 - (void)keyDown:(NSEvent *)theEvent
886 suspendKeyUpEvents = YES;
887 WKSendKeyEventToTSM(theEvent);
890 static OSStatus TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *pluginView)
892 EventRef rawKeyEventRef;
893 OSStatus status = GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(EventRef), NULL, &rawKeyEventRef);
894 if (status != noErr) {
895 LOG_ERROR("GetEventParameter failed with error: %d", status);
899 // Two-pass read to allocate/extract Mac charCodes
901 status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL);
902 if (status != noErr) {
903 LOG_ERROR("GetEventParameter failed with error: %d", status);
906 char *buffer = malloc(numBytes);
907 status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, numBytes, NULL, buffer);
908 if (status != noErr) {
909 LOG_ERROR("GetEventParameter failed with error: %d", status);
914 EventRef cloneEvent = CopyEvent(rawKeyEventRef);
916 for (i = 0; i < numBytes; i++) {
917 status = SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes, typeChar, 1 /* one char code */, &buffer[i]);
918 if (status != noErr) {
919 LOG_ERROR("SetEventParameter failed with error: %d", status);
924 EventRecord eventRec;
925 if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
927 acceptedEvent = [(WebBaseNetscapePluginView *)pluginView sendEvent:&eventRec];
929 LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu",
930 acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask));
932 // We originally thought that if the plug-in didn't accept this event,
933 // we should pass it along so that keyboard scrolling, for example, will work.
934 // In practice, this is not a good idea, because plug-ins tend to eat the event but return false.
935 // MacIE handles each key event twice because of this, but we will emulate the other browsers instead.
938 ReleaseEvent(cloneEvent);
944 // Fake up command-modified events so cut, copy, paste and select all menus work.
945 - (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character
948 [self getCarbonEvent:&event];
949 event.what = keyDown;
950 event.modifiers |= cmdKey;
951 event.message = keyCode << 8 | character;
952 [self sendEvent:&event];
955 - (void)cut:(id)sender
957 [self sendModifierEventWithKeyCode:7 character:'x'];
960 - (void)copy:(id)sender
962 [self sendModifierEventWithKeyCode:8 character:'c'];
965 - (void)paste:(id)sender
967 [self sendModifierEventWithKeyCode:9 character:'v'];
970 - (void)selectAll:(id)sender
972 [self sendModifierEventWithKeyCode:0 character:'a'];
975 #pragma mark WEB_NETSCAPE_PLUGIN
977 - (BOOL)isNewWindowEqualToOldWindow
979 if (window.x != lastSetWindow.x)
981 if (window.y != lastSetWindow.y)
983 if (window.width != lastSetWindow.width)
985 if (window.height != lastSetWindow.height)
987 if (window.clipRect.top != lastSetWindow.clipRect.top)
989 if (window.clipRect.left != lastSetWindow.clipRect.left)
991 if (window.clipRect.bottom != lastSetWindow.clipRect.bottom)
993 if (window.clipRect.right != lastSetWindow.clipRect.right)
995 if (window.type != lastSetWindow.type)
998 switch (drawingModel) {
999 #ifndef NP_NO_QUICKDRAW
1000 case NPDrawingModelQuickDraw:
1001 if (nPort.qdPort.portx != lastSetPort.qdPort.portx)
1003 if (nPort.qdPort.porty != lastSetPort.qdPort.porty)
1005 if (nPort.qdPort.port != lastSetPort.qdPort.port)
1008 #endif /* NP_NO_QUICKDRAW */
1010 case NPDrawingModelCoreGraphics:
1011 if (nPort.cgPort.window != lastSetPort.cgPort.window)
1013 if (nPort.cgPort.context != lastSetPort.cgPort.context)
1017 case NPDrawingModelOpenGL:
1018 if (nPort.aglPort.window != lastSetPort.aglPort.window)
1020 if (nPort.aglPort.context != lastSetPort.aglPort.context)
1025 ASSERT_NOT_REACHED();
1032 - (void)updateAndSetWindow
1034 if (drawingModel == NPDrawingModelCoreGraphics || drawingModel == NPDrawingModelOpenGL) {
1035 // Can only update CoreGraphics and OpenGL plugins while redrawing the plugin view
1036 [self setNeedsDisplay:YES];
1040 // Can't update the plugin if it has not started (or has been stopped)
1044 PortState portState = [self saveAndSetNewPortState];
1046 [self setWindowIfNecessary];
1047 [self restorePortState:portState];
1052 - (void)setWindowIfNecessary
1058 if (![self isNewWindowEqualToOldWindow]) {
1059 // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
1060 // We probably don't want more general reentrancy protection; we are really
1061 // protecting only against this one case, which actually comes up when
1062 // you first install the SVG viewer plug-in.
1064 ASSERT(!inSetWindow);
1068 // A CoreGraphics or OpenGL plugin's window may only be set while the plugin is being updated
1069 ASSERT((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || [NSView focusView] == self);
1071 [self willCallPlugInFunction];
1072 npErr = NPP_SetWindow(instance, &window);
1073 [self didCallPlugInFunction];
1077 switch (drawingModel) {
1078 #ifndef NP_NO_QUICKDRAW
1079 case NPDrawingModelQuickDraw:
1080 LOG(Plugins, "NPP_SetWindow (QuickDraw): %d, port=0x%08x, window.x:%d window.y:%d window.width:%d window.height:%d",
1081 npErr, (int)nPort.qdPort.port, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1083 #endif /* NP_NO_QUICKDRAW */
1085 case NPDrawingModelCoreGraphics:
1086 LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d",
1087 npErr, nPort.cgPort.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1090 case NPDrawingModelOpenGL:
1091 LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d",
1092 npErr, nPort.aglPort.window, nPort.aglPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1096 ASSERT_NOT_REACHED();
1099 #endif /* !defined(NDEBUG) */
1101 lastSetWindow = window;
1102 lastSetPort = nPort;
1106 - (void)removeTrackingRect
1109 [self removeTrackingRect:trackingTag];
1112 // Must release the window to balance the retain in resetTrackingRect.
1113 // But must do it after setting trackingTag to 0 so we don't re-enter.
1114 [[self window] release];
1118 - (void)resetTrackingRect
1120 [self removeTrackingRect];
1122 // Must retain the window so that removeTrackingRect can work after the window is closed.
1123 [[self window] retain];
1124 trackingTag = [self addTrackingRect:[self bounds] owner:self userData:nil assumeInside:NO];
1128 + (void)setCurrentPluginView:(WebBaseNetscapePluginView *)view
1130 currentPluginView = view;
1133 + (WebBaseNetscapePluginView *)currentPluginView
1135 return currentPluginView;
1145 // Do nothing. Overridden by subclasses.
1148 - (void)addWindowObservers
1150 ASSERT([self window]);
1152 NSWindow *theWindow = [self window];
1154 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
1155 [notificationCenter addObserver:self selector:@selector(windowWillClose:)
1156 name:NSWindowWillCloseNotification object:theWindow];
1157 [notificationCenter addObserver:self selector:@selector(windowBecameKey:)
1158 name:NSWindowDidBecomeKeyNotification object:theWindow];
1159 [notificationCenter addObserver:self selector:@selector(windowResignedKey:)
1160 name:NSWindowDidResignKeyNotification object:theWindow];
1161 [notificationCenter addObserver:self selector:@selector(windowDidMiniaturize:)
1162 name:NSWindowDidMiniaturizeNotification object:theWindow];
1163 [notificationCenter addObserver:self selector:@selector(windowDidDeminiaturize:)
1164 name:NSWindowDidDeminiaturizeNotification object:theWindow];
1166 [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchFromUser:)
1167 name:LoginWindowDidSwitchFromUserNotification object:nil];
1168 [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchToUser:)
1169 name:LoginWindowDidSwitchToUserNotification object:nil];
1172 - (void)removeWindowObservers
1174 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
1175 [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:nil];
1176 [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
1177 [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
1178 [notificationCenter removeObserver:self name:NSWindowDidMiniaturizeNotification object:nil];
1179 [notificationCenter removeObserver:self name:NSWindowDidDeminiaturizeNotification object:nil];
1180 [notificationCenter removeObserver:self name:LoginWindowDidSwitchFromUserNotification object:nil];
1181 [notificationCenter removeObserver:self name:LoginWindowDidSwitchToUserNotification object:nil];
1186 ASSERT([self currentWindow]);
1192 if (![self canStart]) {
1196 ASSERT([self webView]);
1198 if (![[[self webView] preferences] arePlugInsEnabled]) {
1204 // Open the plug-in package so it remains loaded while this instance uses it
1207 // Initialize drawingModel to an invalid value so that we can detect when the plugin does not specify a drawingModel
1210 // Plug-ins are "windowed" by default. On MacOS, windowed plug-ins share the same window and graphics port as the main
1211 // browser window. Windowless plug-ins are rendered off-screen, then copied into the main browser window.
1212 window.type = NPWindowTypeWindow;
1214 // NPN_New(), which creates the plug-in instance, should never be called while calling a plug-in function for that instance.
1215 ASSERT(pluginFunctionCallDepth == 0);
1217 [[self class] setCurrentPluginView:self];
1218 NPError npErr = NPP_New((char *)[MIMEType cString], instance, mode, argsCount, cAttributes, cValues, NULL);
1219 [[self class] setCurrentPluginView:nil];
1221 if (drawingModel == (NPDrawingModel)-1) {
1222 #ifndef NP_NO_QUICKDRAW
1223 // Default to QuickDraw if the plugin did not specify a drawing model.
1224 drawingModel = NPDrawingModelQuickDraw;
1226 // QuickDraw is not available, so we can't default to it. We could default to CoreGraphics instead, but
1227 // if the plugin did not specify the CoreGraphics drawing model then it must be one of the old QuickDraw
1228 // plugins. Thus, the plugin is unsupported and should not be started. Destroy it here and bail out.
1229 LOG(Plugins, "Plugin only supports QuickDraw, but QuickDraw is unavailable: %@", plugin);
1230 NPP_Destroy(instance, NULL);
1231 instance->pdata = NULL;
1237 LOG(Plugins, "NPP_New: %d", npErr);
1238 if (npErr != NPERR_NO_ERROR) {
1239 LOG_ERROR("NPP_New failed with error: %d", npErr);
1246 [self updateAndSetWindow];
1248 if ([self window]) {
1249 [self addWindowObservers];
1250 if ([[self window] isKeyWindow]) {
1251 [self sendActivateEvent:YES];
1253 [self restartNullEvents];
1256 [self resetTrackingRect];
1265 // If we're already calling a plug-in function, do not call NPP_Destroy(). The plug-in function we are calling
1266 // may assume that its instance->pdata, or other memory freed by NPP_Destroy(), is valid and unchanged until said
1267 // plugin-function returns.
1268 // See <rdar://problem/4480737>.
1269 if (pluginFunctionCallDepth > 0) {
1270 shouldStopSoon = YES;
1274 [self removeTrackingRect];
1282 // Stop any active streams
1283 [streams makeObjectsPerformSelector:@selector(stop)];
1285 // Stop the null events
1286 [self stopNullEvents];
1288 // Set cursor back to arrow cursor
1289 [[NSCursor arrowCursor] set];
1291 // Stop notifications and callbacks.
1292 [self removeWindowObservers];
1293 [[pendingFrameLoads allKeys] makeObjectsPerformSelector:@selector(_setInternalLoadDelegate:) withObject:nil];
1294 [NSObject cancelPreviousPerformRequestsWithTarget:self];
1296 // Setting the window type to 0 ensures that NPP_SetWindow will be called if the plug-in is restarted.
1297 lastSetWindow.type = 0;
1300 npErr = NPP_Destroy(instance, NULL);
1301 LOG(Plugins, "NPP_Destroy: %d", npErr);
1303 instance->pdata = NULL;
1305 // This instance no longer needs the plug-in package
1308 // We usually remove the key event handler in resignFirstResponder but it is possible that resignFirstResponder
1309 // may never get called so we can't completely rely on it.
1310 [self removeKeyEventHandler];
1312 if (drawingModel == NPDrawingModelOpenGL)
1313 [self _destroyAGLContext];
1321 - (WebDataSource *)dataSource
1323 // Do nothing. Overridden by subclasses.
1327 - (WebFrame *)webFrame
1329 return [[self dataSource] webFrame];
1332 - (WebView *)webView
1334 return [[self webFrame] webView];
1337 - (NSWindow *)currentWindow
1339 return [self window] ? [self window] : [[self webView] hostWindow];
1342 - (NPP)pluginPointer
1347 - (WebNetscapePluginPackage *)plugin
1352 - (void)setPlugin:(WebNetscapePluginPackage *)thePlugin;
1358 NPP_New = [plugin NPP_New];
1359 NPP_Destroy = [plugin NPP_Destroy];
1360 NPP_SetWindow = [plugin NPP_SetWindow];
1361 NPP_NewStream = [plugin NPP_NewStream];
1362 NPP_WriteReady = [plugin NPP_WriteReady];
1363 NPP_Write = [plugin NPP_Write];
1364 NPP_StreamAsFile = [plugin NPP_StreamAsFile];
1365 NPP_DestroyStream = [plugin NPP_DestroyStream];
1366 NPP_HandleEvent = [plugin NPP_HandleEvent];
1367 NPP_URLNotify = [plugin NPP_URLNotify];
1368 NPP_GetValue = [plugin NPP_GetValue];
1369 NPP_SetValue = [plugin NPP_SetValue];
1370 NPP_Print = [plugin NPP_Print];
1373 - (void)setMIMEType:(NSString *)theMIMEType
1375 NSString *type = [theMIMEType copy];
1380 - (void)setBaseURL:(NSURL *)theBaseURL
1382 [theBaseURL retain];
1384 baseURL = theBaseURL;
1387 - (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values;
1389 ASSERT([keys count] == [values count]);
1391 // Convert the attributes to 2 C string arrays.
1392 // These arrays are passed to NPP_New, but the strings need to be
1393 // modifiable and live the entire life of the plugin.
1395 // The Java plug-in requires the first argument to be the base URL
1396 if ([MIMEType isEqualToString:@"application/x-java-applet"]) {
1397 cAttributes = (char **)malloc(([keys count] + 1) * sizeof(char *));
1398 cValues = (char **)malloc(([values count] + 1) * sizeof(char *));
1399 cAttributes[0] = strdup("DOCBASE");
1400 cValues[0] = strdup([baseURL _web_URLCString]);
1403 cAttributes = (char **)malloc([keys count] * sizeof(char *));
1404 cValues = (char **)malloc([values count] * sizeof(char *));
1407 BOOL isWMP = [[[plugin bundle] bundleIdentifier] isEqualToString:@"com.microsoft.WMP.defaultplugin"];
1410 unsigned count = [keys count];
1411 for (i = 0; i < count; i++) {
1412 NSString *key = [keys objectAtIndex:i];
1413 NSString *value = [values objectAtIndex:i];
1414 if ([key _webkit_isCaseInsensitiveEqualToString:@"height"]) {
1415 specifiedHeight = [value intValue];
1416 } else if ([key _webkit_isCaseInsensitiveEqualToString:@"width"]) {
1417 specifiedWidth = [value intValue];
1419 // Avoid Window Media Player crash when these attributes are present.
1420 if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) {
1423 cAttributes[argsCount] = strdup([key UTF8String]);
1424 cValues[argsCount] = strdup([value UTF8String]);
1425 LOG(Plugins, "%@ = %@", key, value);
1430 - (void)setMode:(int)theMode
1437 - initWithFrame:(NSRect)frame
1439 [super initWithFrame:frame];
1441 instance = &instanceStruct;
1442 instance->ndata = self;
1443 streams = [[NSMutableArray alloc] init];
1444 pendingFrameLoads = [[NSMutableDictionary alloc] init];
1449 - (void)freeAttributeKeysAndValues
1452 for (i = 0; i < argsCount; i++) {
1453 free(cAttributes[i]);
1468 [pendingFrameLoads release];
1472 ASSERT(!aglContext);
1474 [self freeAttributeKeysAndValues];
1483 [self freeAttributeKeysAndValues];
1488 - (void)drawRect:(NSRect)rect
1494 if ([NSGraphicsContext currentContextDrawingToScreen])
1495 [self sendUpdateEvent];
1497 NSBitmapImageRep *printedPluginBitmap = [self _printedPluginBitmap];
1498 if (printedPluginBitmap) {
1499 // Flip the bitmap before drawing because the QuickDraw port is flipped relative
1501 CGContextRef cgContext = [[NSGraphicsContext currentContext] graphicsPort];
1502 CGContextSaveGState(cgContext);
1503 NSRect bounds = [self bounds];
1504 CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
1505 CGContextScaleCTM(cgContext, 1.0f, -1.0f);
1506 [printedPluginBitmap drawInRect:bounds];
1507 CGContextRestoreGState(cgContext);
1511 // If this is a windowless OpenGL plugin, blit its contents back into this view. The plug-in just drew into the offscreen context.
1512 if (drawingModel == NPDrawingModelOpenGL && window.type == NPWindowTypeDrawable) {
1513 NSImage *aglOffscreenImage = [self _aglOffscreenImageForDrawingInRect:rect];
1514 if (aglOffscreenImage) {
1515 // Flip the context before drawing because the CGL context is flipped relative to this view.
1516 CGContextRef cgContext = [[NSGraphicsContext currentContext] graphicsPort];
1517 CGContextSaveGState(cgContext);
1518 NSRect bounds = [self bounds];
1519 CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
1520 CGContextScaleCTM(cgContext, 1.0f, -1.0f);
1522 // Copy 'rect' from the offscreen buffer to this view (the flip above makes this sort of tricky)
1523 NSRect flippedRect = rect;
1524 flippedRect.origin.y = NSMaxY(bounds) - NSMaxY(flippedRect);
1525 [aglOffscreenImage drawInRect:flippedRect fromRect:flippedRect operation:NSCompositeSourceOver fraction:1.0f];
1526 CGContextRestoreGState(cgContext);
1538 [super renewGState];
1540 // -renewGState is called whenever the view's geometry changes. It's a little hacky to override this method, but
1541 // much safer than walking up the view hierarchy and observing frame/bounds changed notifications, since you don't
1542 // have to track subsequent changes to the view hierarchy and add/remove notification observers.
1543 // NSOpenGLView uses the exact same technique to reshape its OpenGL surface.
1544 [self _viewHasMoved];
1547 #ifndef NP_NO_QUICKDRAW
1548 -(void)tellQuickTimeToChill
1550 ASSERT(drawingModel == NPDrawingModelQuickDraw);
1552 // Make a call to the secret QuickDraw API that makes QuickTime calm down.
1553 WindowRef windowRef = [[self window] windowRef];
1557 CGrafPtr port = GetWindowPort(windowRef);
1559 GetPortBounds(port, &bounds);
1560 WKCallDrawingNotification(port, &bounds);
1562 #endif /* NP_NO_QUICKDRAW */
1564 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
1566 #ifndef NP_NO_QUICKDRAW
1567 if (drawingModel == NPDrawingModelQuickDraw)
1568 [self tellQuickTimeToChill];
1571 // We must remove the tracking rect before we move to the new window.
1572 // Once we move to the new window, it will be too late.
1573 [self removeTrackingRect];
1574 [self removeWindowObservers];
1576 // Workaround for: <rdar://problem/3822871> resignFirstResponder is not sent to first responder view when it is removed from the window
1577 [self setHasFocus:NO];
1580 // Hide the AGL child window
1581 if (drawingModel == NPDrawingModelOpenGL)
1582 [self _hideAGLWindow];
1584 if ([[self webView] hostWindow]) {
1585 // View will be moved out of the actual window but it still has a host window.
1586 [self stopNullEvents];
1588 // View will have no associated windows.
1591 // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
1592 // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
1593 [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1598 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
1600 if (!newSuperview) {
1601 // Stop the plug-in when it is removed from its superview. It is not sufficient to do this in -viewWillMoveToWindow:nil, because
1602 // the WebView might still has a hostWindow at that point, which prevents the plug-in from being destroyed.
1603 // There is no need to start the plug-in when moving into a superview. -viewDidMoveToWindow takes care of that.
1606 // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
1607 // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
1608 [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1612 - (void)viewDidMoveToWindow
1614 [self resetTrackingRect];
1616 if ([self window]) {
1617 // While in the view hierarchy, observe WebPreferencesChangedNotification so that we can start/stop depending
1618 // on whether plugins are enabled.
1619 [[NSNotificationCenter defaultCenter] addObserver:self
1620 selector:@selector(preferencesHaveChanged:)
1621 name:WebPreferencesChangedNotification
1624 // View moved to an actual window. Start it if not already started.
1626 [self restartNullEvents];
1627 [self addWindowObservers];
1628 } else if ([[self webView] hostWindow]) {
1629 // View moved out of an actual window, but still has a host window.
1630 // Call setWindow to explicitly "clip out" the plug-in from sight.
1631 // FIXME: It would be nice to do this where we call stopNullEvents in viewWillMoveToWindow.
1632 [self updateAndSetWindow];
1636 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
1638 if (!hostWindow && ![self window]) {
1639 // View will have no associated windows.
1642 // Remove WebPreferencesChangedNotification observer -- we will observe once again when we move back into the window
1643 [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1647 - (void)viewDidMoveToHostWindow
1649 if ([[self webView] hostWindow]) {
1650 // View now has an associated window. Start it if not already started.
1655 #pragma mark NOTIFICATIONS
1657 - (void)windowWillClose:(NSNotification *)notification
1662 - (void)windowBecameKey:(NSNotification *)notification
1664 [self sendActivateEvent:YES];
1665 [self setNeedsDisplay:YES];
1666 [self restartNullEvents];
1667 SetUserFocusWindow([[self window] windowRef]);
1670 - (void)windowResignedKey:(NSNotification *)notification
1672 [self sendActivateEvent:NO];
1673 [self setNeedsDisplay:YES];
1674 [self restartNullEvents];
1677 - (void)windowDidMiniaturize:(NSNotification *)notification
1679 [self stopNullEvents];
1682 - (void)windowDidDeminiaturize:(NSNotification *)notification
1684 [self restartNullEvents];
1687 - (void)loginWindowDidSwitchFromUser:(NSNotification *)notification
1689 [self stopNullEvents];
1692 -(void)loginWindowDidSwitchToUser:(NSNotification *)notification
1694 [self restartNullEvents];
1697 - (void)preferencesHaveChanged:(NSNotification *)notification
1699 WebPreferences *preferences = [[self webView] preferences];
1700 BOOL arePlugInsEnabled = [preferences arePlugInsEnabled];
1702 if ([notification object] == preferences && isStarted != arePlugInsEnabled) {
1703 if (arePlugInsEnabled) {
1704 if ([self currentWindow]) {
1709 [self setNeedsDisplay:YES];
1714 - (NPObject *)createPluginScriptableObject
1720 [self willCallPlugInFunction];
1721 NPError error = NPP_GetValue(instance, NPPVpluginScriptableNPObject, &value);
1722 [self didCallPlugInFunction];
1723 if (error != NPERR_NO_ERROR)
1729 - (void)willCallPlugInFunction
1731 // Could try to prevent infinite recursion here, but it's probably not worth the effort.
1732 pluginFunctionCallDepth++;
1735 - (void)didCallPlugInFunction
1737 ASSERT(pluginFunctionCallDepth > 0);
1738 pluginFunctionCallDepth--;
1740 // If -stop was called while we were calling into a plug-in function, and we're no longer
1741 // inside a plug-in function, stop now.
1742 if (pluginFunctionCallDepth == 0 && shouldStopSoon) {
1743 shouldStopSoon = NO;
1750 @implementation WebBaseNetscapePluginView (WebNPPCallbacks)
1752 - (NSMutableURLRequest *)requestWithURLCString:(const char *)URLCString
1757 CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, URLCString, kCFStringEncodingISOLatin1);
1758 ASSERT(string); // All strings should be representable in ISO Latin 1
1760 NSString *URLString = [(NSString *)string _web_stringByStrippingReturnCharacters];
1761 NSURL *URL = [NSURL _web_URLWithDataAsString:URLString relativeToURL:baseURL];
1766 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
1767 [request _web_setHTTPReferrer:[[[self webFrame] _bridge] referrer]];
1771 - (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest
1773 // FIXME: Is this isStarted check needed here? evaluateJavaScriptPluginRequest should not be called
1774 // if we are stopped since this method is called after a delay and we call
1775 // cancelPreviousPerformRequestsWithTarget inside of stop.
1780 NSURL *URL = [[JSPluginRequest request] URL];
1781 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1784 NSString *result = [[[self webFrame] _bridge] stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]];
1786 // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop.
1791 if ([JSPluginRequest frameName] != nil) {
1792 // FIXME: If the result is a string, we probably want to put that string into the frame, just
1793 // like we do in KHTMLPartBrowserExtension::openURLRequest.
1794 if ([JSPluginRequest sendNotification]) {
1795 [self willCallPlugInFunction];
1796 NPP_URLNotify(instance, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]);
1797 [self didCallPlugInFunction];
1799 } else if ([result length] > 0) {
1800 // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does.
1801 NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding];
1802 WebBaseNetscapePluginStream *stream = [[WebBaseNetscapePluginStream alloc] initWithRequestURL:URL
1803 pluginPointer:instance
1804 notifyData:[JSPluginRequest notifyData]
1805 sendNotification:[JSPluginRequest sendNotification]];
1806 [stream startStreamResponseURL:URL
1807 expectedContentLength:[JSData length]
1808 lastModifiedDate:nil
1809 MIMEType:@"text/plain"];
1810 [stream receivedData:JSData];
1811 [stream finishedLoadingWithData:JSData];
1816 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
1820 WebPluginRequest *pluginRequest = [pendingFrameLoads objectForKey:webFrame];
1821 ASSERT(pluginRequest != nil);
1822 ASSERT([pluginRequest sendNotification]);
1824 [self willCallPlugInFunction];
1825 NPP_URLNotify(instance, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]);
1826 [self didCallPlugInFunction];
1828 [pendingFrameLoads removeObjectForKey:webFrame];
1829 [webFrame _setInternalLoadDelegate:nil];
1832 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
1834 NPReason reason = NPRES_DONE;
1836 reason = [WebBaseNetscapePluginStream reasonForError:error];
1838 [self webFrame:webFrame didFinishLoadWithReason:reason];
1841 - (void)loadPluginRequest:(WebPluginRequest *)pluginRequest
1843 NSURLRequest *request = [pluginRequest request];
1844 NSString *frameName = [pluginRequest frameName];
1845 WebFrame *frame = nil;
1847 NSURL *URL = [request URL];
1848 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1850 ASSERT(frameName || JSString);
1853 // FIXME - need to get rid of this window creation which
1854 // bypasses normal targeted link handling
1855 frame = [[self webFrame] findFrameNamed:frameName];
1858 WebView *newWebView = nil;
1859 WebView *currentWebView = [self webView];
1860 id wd = [currentWebView UIDelegate];
1861 if ([wd respondsToSelector:@selector(webView:createWebViewWithRequest:)]) {
1862 newWebView = [wd webView:currentWebView createWebViewWithRequest:nil];
1864 newWebView = [[WebDefaultUIDelegate sharedUIDelegate] webView:currentWebView createWebViewWithRequest:nil];
1866 frame = [newWebView mainFrame];
1867 [[frame _bridge] setName:frameName];
1868 [[newWebView _UIDelegateForwarder] webViewShow:newWebView];
1873 ASSERT(frame == nil || [self webFrame] == frame);
1874 [self evaluateJavaScriptPluginRequest:pluginRequest];
1876 [frame loadRequest:request];
1877 if ([pluginRequest sendNotification]) {
1878 // Check if another plug-in view or even this view is waiting for the frame to load.
1879 // If it is, tell it that the load was cancelled because it will be anyway.
1880 WebBaseNetscapePluginView *view = [frame _internalLoadDelegate];
1882 ASSERT([view isKindOfClass:[WebBaseNetscapePluginView class]]);
1883 [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK];
1885 [pendingFrameLoads _webkit_setObject:pluginRequest forUncopiedKey:frame];
1886 [frame _setInternalLoadDelegate:self];
1891 - (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification
1893 NSURL *URL = [request URL];
1896 return NPERR_INVALID_URL;
1898 // don't let a plugin start any loads if it is no longer part of a document that is being
1900 if ([self dataSource] != [[[self webFrame] _frameLoader] activeDataSource])
1901 return NPERR_GENERIC_ERROR;
1903 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1904 if (JSString != nil) {
1905 if (![[[self webView] preferences] isJavaScriptEnabled]) {
1906 // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does.
1907 return NPERR_GENERIC_ERROR;
1908 } else if (cTarget == NULL && mode == NP_FULL) {
1909 // Don't allow a JavaScript request from a standalone plug-in that is self-targetted
1910 // because this can cause the user to be redirected to a blank page (3424039).
1911 return NPERR_INVALID_PARAM;
1915 if (cTarget || JSString) {
1916 // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't
1917 // want to potentially kill the plug-in inside of its URL request.
1918 NSString *target = nil;
1920 // Find the frame given the target string.
1921 target = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, cTarget, kCFStringEncodingWindowsLatin1);
1924 WebFrame *frame = [self webFrame];
1925 if (JSString != nil && target != nil && [frame findFrameNamed:target] != frame) {
1926 // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
1928 return NPERR_INVALID_PARAM;
1931 WebPluginRequest *pluginRequest = [[WebPluginRequest alloc] initWithRequest:request frameName:target notifyData:notifyData sendNotification:sendNotification didStartFromUserGesture:currentEventIsUserGesture];
1932 [self performSelector:@selector(loadPluginRequest:) withObject:pluginRequest afterDelay:0];
1933 [pluginRequest release];
1937 WebNetscapePluginStream *stream = [[WebNetscapePluginStream alloc] initWithRequest:request
1938 pluginPointer:instance
1939 notifyData:notifyData
1940 sendNotification:sendNotification];
1942 return NPERR_INVALID_URL;
1944 [streams addObject:stream];
1949 return NPERR_NO_ERROR;
1952 -(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData
1954 LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget);
1956 NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1957 return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES];
1960 -(NPError)getURL:(const char *)URLCString target:(const char *)cTarget
1962 LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget);
1964 NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1965 return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO];
1968 - (NPError)_postURL:(const char *)URLCString
1969 target:(const char *)target
1971 buf:(const char *)buf
1973 notifyData:(void *)notifyData
1974 sendNotification:(BOOL)sendNotification
1975 allowHeaders:(BOOL)allowHeaders
1977 if (!URLCString || !len || !buf) {
1978 return NPERR_INVALID_PARAM;
1981 NSData *postData = nil;
1984 // If we're posting a file, buf is either a file URL or a path to the file.
1985 NSString *bufString = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingWindowsLatin1);
1987 return NPERR_INVALID_PARAM;
1989 NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString];
1991 if ([fileURL isFileURL]) {
1992 path = [fileURL path];
1996 postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]];
1997 CFRelease(bufString);
1999 return NPERR_FILE_NOT_FOUND;
2002 postData = [NSData dataWithBytes:buf length:len];
2005 if ([postData length] == 0) {
2006 return NPERR_INVALID_PARAM;
2009 NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
2010 [request setHTTPMethod:@"POST"];
2013 if ([postData _web_startsWithBlankLine]) {
2014 postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)];
2016 WebNSInteger location = [postData _web_locationAfterFirstBlankLine];
2017 if (location != NSNotFound) {
2018 // If the blank line is somewhere in the middle of postData, everything before is the header.
2019 NSData *headerData = [postData subdataWithRange:NSMakeRange(0, location)];
2020 NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields];
2021 unsigned dataLength = [postData length] - location;
2023 // Sometimes plugins like to set Content-Length themselves when they post,
2024 // but WebFoundation does not like that. So we will remove the header
2025 // and instead truncate the data to the requested length.
2026 NSString *contentLength = [header objectForKey:@"Content-Length"];
2028 if (contentLength != nil)
2029 dataLength = MIN((unsigned)[contentLength intValue], dataLength);
2030 [header removeObjectForKey:@"Content-Length"];
2032 if ([header count] > 0) {
2033 [request setAllHTTPHeaderFields:header];
2035 // Everything after the blank line is the actual content of the POST.
2036 postData = [postData subdataWithRange:NSMakeRange(location, dataLength)];
2040 if ([postData length] == 0) {
2041 return NPERR_INVALID_PARAM;
2045 // Plug-ins expect to receive uncached data when doing a POST (3347134).
2046 [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
2047 [request setHTTPBody:postData];
2049 return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification];
2052 - (NPError)postURLNotify:(const char *)URLCString
2053 target:(const char *)target
2055 buf:(const char *)buf
2057 notifyData:(void *)notifyData
2059 LOG(Plugins, "NPN_PostURLNotify: %s", URLCString);
2060 return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:notifyData sendNotification:YES allowHeaders:YES];
2063 -(NPError)postURL:(const char *)URLCString
2064 target:(const char *)target
2066 buf:(const char *)buf
2069 LOG(Plugins, "NPN_PostURL: %s", URLCString);
2070 // As documented, only allow headers to be specified via NPP_PostURL when using a file.
2071 return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:NULL sendNotification:NO allowHeaders:file];
2074 -(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream
2076 LOG(Plugins, "NPN_NewStream");
2077 return NPERR_GENERIC_ERROR;
2080 -(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer
2082 LOG(Plugins, "NPN_Write");
2083 return NPERR_GENERIC_ERROR;
2086 -(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason
2088 LOG(Plugins, "NPN_DestroyStream");
2089 if (!stream->ndata) {
2090 return NPERR_INVALID_INSTANCE_ERROR;
2092 WebBaseNetscapePluginStream *browserStream = (WebBaseNetscapePluginStream *)stream->ndata;
2093 [browserStream cancelLoadAndDestroyStreamWithError:[browserStream errorForReason:reason]];
2094 return NPERR_NO_ERROR;
2097 - (const char *)userAgent
2099 return [[[self webView] userAgentForURL:baseURL] lossyCString];
2102 -(void)status:(const char *)message
2105 LOG_ERROR("NPN_Status passed a NULL status message");
2109 CFStringRef status = CFStringCreateWithCString(NULL, message, kCFStringEncodingWindowsLatin1);
2110 LOG(Plugins, "NPN_Status: %@", status);
2111 WebView *wv = [self webView];
2112 [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status];
2116 -(void)invalidateRect:(NPRect *)invalidRect
2118 LOG(Plugins, "NPN_InvalidateRect");
2119 [self setNeedsDisplayInRect:NSMakeRect(invalidRect->left, invalidRect->top,
2120 (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)];
2123 - (void)invalidateRegion:(NPRegion)invalidRegion
2125 LOG(Plugins, "NPN_InvalidateRegion");
2127 switch (drawingModel) {
2128 #ifndef NP_NO_QUICKDRAW
2129 case NPDrawingModelQuickDraw:
2132 GetRegionBounds(invalidRegion, &qdRect);
2133 invalidRect = NSMakeRect(qdRect.left, qdRect.top, qdRect.right - qdRect.left, qdRect.bottom - qdRect.top);
2136 #endif /* NP_NO_QUICKDRAW */
2138 case NPDrawingModelCoreGraphics:
2139 case NPDrawingModelOpenGL:
2141 CGRect cgRect = CGPathGetBoundingBox((NPCGRegion)invalidRegion);
2142 invalidRect = *(NSRect *)&cgRect;
2147 ASSERT_NOT_REACHED();
2151 [self setNeedsDisplayInRect:invalidRect];
2156 LOG(Plugins, "forceRedraw");
2157 [self setNeedsDisplay:YES];
2158 [[self window] displayIfNeeded];
2161 - (NPError)getVariable:(NPNVariable)variable value:(void *)value
2164 case NPNVWindowNPObject:
2166 NPObject *windowScriptObject = [[[self webFrame] _bridge] windowScriptNPObject];
2168 // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
2169 if (windowScriptObject)
2170 _NPN_RetainObject(windowScriptObject);
2172 void **v = (void **)value;
2173 *v = windowScriptObject;
2175 return NPERR_NO_ERROR;
2178 case NPNVPluginElementNPObject:
2181 return NPERR_GENERIC_ERROR;
2183 NPObject *plugInScriptObject = [element _NPObject];
2185 // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
2186 if (plugInScriptObject)
2187 _NPN_RetainObject(plugInScriptObject);
2189 void **v = (void **)value;
2190 *v = plugInScriptObject;
2192 return NPERR_NO_ERROR;
2195 case NPNVpluginDrawingModel:
2197 *(NPDrawingModel *)value = drawingModel;
2198 return NPERR_NO_ERROR;
2201 #ifndef NP_NO_QUICKDRAW
2202 case NPNVsupportsQuickDrawBool:
2204 *(NPBool *)value = TRUE;
2205 return NPERR_NO_ERROR;
2207 #endif /* NP_NO_QUICKDRAW */
2209 case NPNVsupportsCoreGraphicsBool:
2211 *(NPBool *)value = TRUE;
2212 return NPERR_NO_ERROR;
2215 case NPNVsupportsOpenGLBool:
2217 *(NPBool *)value = TRUE;
2218 return NPERR_NO_ERROR;
2225 return NPERR_GENERIC_ERROR;
2228 - (NPError)setVariable:(NPPVariable)variable value:(void *)value
2231 case NPPVpluginWindowBool:
2233 NPWindowType newWindowType = (value ? NPWindowTypeWindow : NPWindowTypeDrawable);
2235 // Redisplay if window type is changing (some drawing models can only have their windows set while updating).
2236 if (newWindowType != window.type)
2237 [self setNeedsDisplay:YES];
2239 window.type = newWindowType;
2242 case NPPVpluginTransparentBool:
2244 BOOL newTransparent = (value != 0);
2246 // Redisplay if transparency is changing
2247 if (isTransparent != newTransparent)
2248 [self setNeedsDisplay:YES];
2250 isTransparent = newTransparent;
2252 return NPERR_NO_ERROR;
2255 case NPNVpluginDrawingModel:
2257 // Can only set drawing model inside NPP_New()
2258 if (self != [[self class] currentPluginView])
2259 return NPERR_GENERIC_ERROR;
2261 // Check for valid, supported drawing model
2262 NPDrawingModel newDrawingModel = (NPDrawingModel)value;
2263 switch (newDrawingModel) {
2264 // Supported drawing models:
2265 #ifndef NP_NO_QUICKDRAW
2266 case NPDrawingModelQuickDraw:
2268 case NPDrawingModelCoreGraphics:
2269 case NPDrawingModelOpenGL:
2270 drawingModel = newDrawingModel;
2271 return NPERR_NO_ERROR;
2273 // Unsupported (or unknown) drawing models:
2275 LOG(Plugins, "Plugin %@ uses unsupported drawing model: %d", plugin, drawingModel);
2276 return NPERR_GENERIC_ERROR;
2281 return NPERR_GENERIC_ERROR;
2287 @implementation WebPluginRequest
2289 - (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture
2292 _didStartFromUserGesture = currentEventIsUserGesture;
2293 _request = [request retain];
2294 _frameName = [frameName retain];
2295 _notifyData = notifyData;
2296 _sendNotification = sendNotification;
2303 [_frameName release];
2307 - (NSURLRequest *)request
2312 - (NSString *)frameName
2317 - (BOOL)isCurrentEventUserGesture
2319 return _didStartFromUserGesture;
2322 - (BOOL)sendNotification
2324 return _sendNotification;
2327 - (void *)notifyData
2334 @implementation WebBaseNetscapePluginView (Internal)
2336 - (void)_viewHasMoved
2338 // All of the work this method does may safely be skipped if the view is not in a window. When the view
2339 // is moved back into a window, everything should be set up correctly.
2343 if (drawingModel == NPDrawingModelOpenGL)
2344 [self _reshapeAGLWindow];
2346 #ifndef NP_NO_QUICKDRAW
2347 if (drawingModel == NPDrawingModelQuickDraw)
2348 [self tellQuickTimeToChill];
2350 [self updateAndSetWindow];
2351 [self resetTrackingRect];
2353 // Check to see if the plugin view is completely obscured (scrolled out of view, for example).
2354 // For performance reasons, we send null events at a lower rate to plugins which are obscured.
2355 BOOL oldIsObscured = isCompletelyObscured;
2356 isCompletelyObscured = NSIsEmptyRect([self visibleRect]);
2357 if (isCompletelyObscured != oldIsObscured)
2358 [self restartNullEvents];
2361 - (NSBitmapImageRep *)_printedPluginBitmap
2363 #ifdef NP_NO_QUICKDRAW
2366 // Cannot print plugins that do not implement NPP_Print
2370 // This NSBitmapImageRep will share its bitmap buffer with a GWorld that the plugin will draw into.
2371 // The bitmap is created in 32-bits-per-pixel ARGB format, which is the default GWorld pixel format.
2372 NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
2373 pixelsWide:window.width
2374 pixelsHigh:window.height
2379 colorSpaceName:NSDeviceRGBColorSpace
2380 bitmapFormat:NSAlphaFirstBitmapFormat
2382 bitsPerPixel:0] autorelease];
2385 // Create a GWorld with the same underlying buffer into which the plugin can draw
2386 Rect printGWorldBounds;
2387 SetRect(&printGWorldBounds, 0, 0, window.width, window.height);
2388 GWorldPtr printGWorld;
2389 if (NewGWorldFromPtr(&printGWorld,
2395 (Ptr)[bitmap bitmapData],
2396 [bitmap bytesPerRow]) != noErr) {
2397 LOG_ERROR("Could not create GWorld for printing");
2401 /// Create NPWindow for the GWorld
2402 NPWindow printNPWindow;
2403 printNPWindow.window = &printGWorld; // Normally this is an NP_Port, but when printing it is the actual CGrafPtr
2404 printNPWindow.x = 0;
2405 printNPWindow.y = 0;
2406 printNPWindow.width = window.width;
2407 printNPWindow.height = window.height;
2408 printNPWindow.clipRect.top = 0;
2409 printNPWindow.clipRect.left = 0;
2410 printNPWindow.clipRect.right = window.width;
2411 printNPWindow.clipRect.bottom = window.height;
2412 printNPWindow.type = NPWindowTypeDrawable; // Offscreen graphics port as opposed to a proper window
2414 // Create embed-mode NPPrint
2416 npPrint.mode = NP_EMBED;
2417 npPrint.print.embedPrint.window = printNPWindow;
2418 npPrint.print.embedPrint.platformPrint = printGWorld;
2420 // Tell the plugin to print into the GWorld
2421 [self willCallPlugInFunction];
2422 NPP_Print(instance, &npPrint);
2423 [self didCallPlugInFunction];
2425 // Don't need the GWorld anymore
2426 DisposeGWorld(printGWorld);
2432 - (BOOL)_createAGLContextIfNeeded
2434 ASSERT(drawingModel == NPDrawingModelOpenGL);
2436 // Do nothing (but indicate success) if the AGL context already exists
2440 switch (window.type) {
2441 case NPWindowTypeWindow:
2442 return [self _createWindowedAGLContext];
2444 case NPWindowTypeDrawable:
2445 return [self _createWindowlessAGLContext];
2448 ASSERT_NOT_REACHED();
2453 - (BOOL)_createWindowedAGLContext
2455 ASSERT(drawingModel == NPDrawingModelOpenGL);
2456 ASSERT(!aglContext);
2458 ASSERT([self window]);
2460 GLint pixelFormatAttributes[] = {
2472 // Choose AGL pixel format
2473 AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL, 0, pixelFormatAttributes);
2475 LOG_ERROR("Could not find suitable AGL pixel format: %s", aglErrorString(aglGetError()));
2479 // Create AGL context
2480 aglContext = aglCreateContext(pixelFormat, NULL);
2481 aglDestroyPixelFormat(pixelFormat);
2483 LOG_ERROR("Could not create AGL context: %s", aglErrorString(aglGetError()));
2487 // Create AGL window
2488 aglWindow = [[NSWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
2490 LOG_ERROR("Could not create window for AGL drawable.");
2494 // AGL window should allow clicks to go through -- mouse events are tracked by WebCore
2495 [aglWindow setIgnoresMouseEvents:YES];
2497 // Make sure the window is not opaque -- windowed plug-ins cannot layer with other page elements
2498 [aglWindow setOpaque:YES];
2500 // Position and order in the AGL window
2501 [self _reshapeAGLWindow];
2503 // Attach the AGL context to its window
2505 #ifdef AGL_VERSION_3_0
2506 success = aglSetWindowRef(aglContext, [aglWindow windowRef]);
2508 success = aglSetDrawable(aglContext, (AGLDrawable)GetWindowPort([aglWindow windowRef]));
2511 LOG_ERROR("Could not set AGL drawable: %s", aglErrorString(aglGetError()));
2512 aglDestroyContext(aglContext);
2520 - (BOOL)_createWindowlessAGLContext
2522 ASSERT(drawingModel == NPDrawingModelOpenGL);
2523 ASSERT(!aglContext);
2526 GLint pixelFormatAttributes[] = {
2537 // Choose AGL pixel format
2538 AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL, 0, pixelFormatAttributes);
2540 LOG_ERROR("Could not find suitable AGL pixel format: %s", aglErrorString(aglGetError()));
2544 // Create AGL context
2545 aglContext = aglCreateContext(pixelFormat, NULL);
2546 aglDestroyPixelFormat(pixelFormat);
2548 LOG_ERROR("Could not create AGL context: %s", aglErrorString(aglGetError()));
2552 // Create offscreen buffer for AGL context
2553 NSSize boundsSize = [self bounds].size;
2554 GLvoid *offscreenBuffer = (GLvoid *)malloc(boundsSize.width * boundsSize.height * 4);
2555 if (!offscreenBuffer) {
2556 LOG_ERROR("Could not allocate offscreen buffer for AGL context");
2557 aglDestroyContext(aglContext);
2562 // Attach AGL context to offscreen buffer
2563 CGLContextObj cglContext = [self _cglContext];
2564 CGLError error = CGLSetOffScreen(cglContext, boundsSize.width, boundsSize.height, boundsSize.width * 4, offscreenBuffer);
2566 LOG_ERROR("Could not set offscreen buffer for AGL context: %d", error);
2567 aglDestroyContext(aglContext);
2575 - (CGLContextObj)_cglContext
2577 ASSERT(drawingModel == NPDrawingModelOpenGL);
2579 CGLContextObj cglContext = NULL;
2580 if (!aglGetCGLContext(aglContext, (void **)&cglContext) || !cglContext)
2581 LOG_ERROR("Could not get CGL context for AGL context: %s", aglErrorString(aglGetError()));
2586 - (BOOL)_getAGLOffscreenBuffer:(GLvoid **)outBuffer width:(GLsizei *)outWidth height:(GLsizei *)outHeight
2588 ASSERT(drawingModel == NPDrawingModelOpenGL);
2597 // Only windowless plug-ins have offscreen buffers
2598 if (window.type != NPWindowTypeDrawable)
2601 CGLContextObj cglContext = [self _cglContext];
2605 GLsizei width, height;
2607 void *offscreenBuffer = NULL;
2608 CGLError error = CGLGetOffScreen(cglContext, &width, &height, &rowBytes, &offscreenBuffer);
2609 if (error || !offscreenBuffer) {
2610 LOG_ERROR("Could not get offscreen buffer for AGL context: %d", error);
2615 *outBuffer = offscreenBuffer;
2619 *outHeight = height;
2624 - (void)_destroyAGLContext
2626 ASSERT(drawingModel == NPDrawingModelOpenGL);
2632 // If this is a windowless plug-in, free its offscreen buffer
2633 GLvoid *offscreenBuffer;
2634 if ([self _getAGLOffscreenBuffer:&offscreenBuffer width:NULL height:NULL])
2635 free(offscreenBuffer);
2637 // Detach context from the AGL window
2638 #ifdef AGL_VERSION_3_0
2639 aglSetWindowRef(aglContext, NULL);
2641 aglSetDrawable(aglContext, NULL);
2644 // Destroy the context
2645 aglDestroyContext(aglContext);
2649 // Destroy the AGL window
2651 [self _hideAGLWindow];
2656 - (void)_reshapeAGLWindow
2658 ASSERT(drawingModel == NPDrawingModelOpenGL);
2663 switch (window.type) {
2664 case NPWindowTypeWindow:
2669 // The AGL window is being reshaped because the plugin view has moved. Since the view has moved, it will soon redraw.
2670 // We want the AGL window to update at the same time as its underlying view. So, we disable screen updates until the
2671 // plugin view's window flushes.
2672 NSWindow *browserWindow = [self window];
2673 ASSERT(browserWindow);
2674 [browserWindow disableScreenUpdatesUntilFlush];
2676 // Add the AGL window as a child of the main window if necessary
2677 if ([aglWindow parentWindow] != browserWindow)
2678 [browserWindow addChildWindow:aglWindow ordered:NSWindowAbove];
2680 // Update the AGL window frame
2681 NSRect aglWindowFrame = [self convertRect:[self visibleRect] toView:nil];
2682 aglWindowFrame.origin = [browserWindow convertBaseToScreen:aglWindowFrame.origin];
2683 [aglWindow setFrame:aglWindowFrame display:NO];
2685 // Update the AGL context
2686 aglUpdateContext(aglContext);
2690 case NPWindowTypeDrawable:
2692 // Get offscreen buffer; we can skip this step if we don't have one yet
2693 GLvoid *offscreenBuffer;
2694 GLsizei width, height;
2695 if (![self _getAGLOffscreenBuffer:&offscreenBuffer width:&width height:&height] || !offscreenBuffer)
2698 // Don't resize the offscreen buffer if it's already the same size as the view bounds
2699 NSSize boundsSize = [self bounds].size;
2700 if (boundsSize.width == width && boundsSize.height == height)
2703 // Resize the offscreen buffer
2704 offscreenBuffer = realloc(offscreenBuffer, boundsSize.width * boundsSize.height * 4);
2705 if (!offscreenBuffer) {
2706 LOG_ERROR("Could not allocate offscreen buffer for AGL context");
2710 // Update the offscreen
2711 CGLContextObj cglContext = [self _cglContext];
2712 CGLError error = CGLSetOffScreen(cglContext, boundsSize.width, boundsSize.height, boundsSize.width * 4, offscreenBuffer);
2714 LOG_ERROR("Could not set offscreen buffer for AGL context: %d", error);
2718 // Update the AGL context
2719 aglUpdateContext(aglContext);
2724 ASSERT_NOT_REACHED();
2729 - (void)_hideAGLWindow
2731 ASSERT(drawingModel == NPDrawingModelOpenGL);
2736 // aglWindow should only be set for a windowed OpenGL plug-in
2737 ASSERT(window.type == NPWindowTypeWindow);
2739 NSWindow *parentWindow = [aglWindow parentWindow];
2741 // Disable screen updates so that this AGL window orders out atomically with other plugins' AGL windows
2742 [parentWindow disableScreenUpdatesUntilFlush];
2743 ASSERT(parentWindow == [self window]);
2744 [parentWindow removeChildWindow:aglWindow];
2746 [aglWindow orderOut:nil];
2749 - (NSImage *)_aglOffscreenImageForDrawingInRect:(NSRect)drawingInRect
2751 ASSERT(drawingModel == NPDrawingModelOpenGL);
2753 CGLContextObj cglContext = [self _cglContext];
2757 // Get the offscreen buffer
2758 GLvoid *offscreenBuffer;
2759 GLsizei width, height;
2760 if (![self _getAGLOffscreenBuffer:&offscreenBuffer width:&width height:&height])
2763 #if defined(__i386__) || defined(__x86_64__)
2764 // Make sure drawingInRect is inside the offscreen buffer because we're about to directly modify the bits inside drawingInRect
2765 drawingInRect = NSIntersectionRect(drawingInRect, NSMakeRect(0, 0, width, height));
2767 // The offscreen buffer, being an OpenGL framebuffer, is in BGRA format on x86. We need to swap the blue and red channels before
2768 // wrapping the buffer in an NSBitmapImageRep, which only supports RGBA and ARGB.
2769 // On PowerPC, the OpenGL framebuffer is in ARGB format. Since that is a format that NSBitmapImageRep supports, all that is
2770 // needed on PowerPC is to pass the NSAlphaFirstBitmapFormat flag when creating the NSBitmapImageRep. On x86, we need to swap the
2771 // framebuffer color components such that they are in ARGB order, as they are on PowerPC.
2772 // If only a small region of the plug-in is being redrawn, then it would be a waste to convert the entire image from BGRA to ARGB.
2773 // Since we know what region of the image will ultimately be drawn to screen (drawingInRect), we restrict the channel swapping to
2774 // just that region within the offscreen buffer.
2775 GLsizei rowBytes = width * 4;
2776 GLvoid *swizzleImageBase = (unsigned char *)offscreenBuffer + (int)(NSMinY(drawingInRect) * rowBytes) + (int)(NSMinX(drawingInRect) * 4);
2777 vImage_Buffer vImage = {
2778 data: swizzleImageBase,
2779 height: NSHeight(drawingInRect),
2780 width: NSWidth(drawingInRect),
2783 uint8_t vImagePermuteMap[4] = { 3, 2, 1, 0 }; // Where { 0, 1, 2, 3 } would leave the channels unchanged; this map converts BGRA to ARGB
2784 vImage_Error vImageError = vImagePermuteChannels_ARGB8888(&vImage, &vImage, vImagePermuteMap, 0);
2786 LOG_ERROR("Could not convert BGRA image to ARGB: %d", vImageError);
2789 #endif /* defined(__i386__) || defined(__x86_64__) */
2791 NSBitmapImageRep *aglBitmap = [[NSBitmapImageRep alloc]
2792 initWithBitmapDataPlanes:(unsigned char **)&offscreenBuffer
2799 colorSpaceName:NSDeviceRGBColorSpace
2800 bitmapFormat:NSAlphaFirstBitmapFormat
2801 bytesPerRow:width * 4
2804 LOG_ERROR("Could not create bitmap for AGL offscreen buffer");
2808 // Wrap the bitmap in an NSImage. This allocation isn't very expensive -- the actual image data is already in the bitmap rep
2809 NSImage *aglImage = [[[NSImage alloc] initWithSize:[aglBitmap size]] autorelease];
2810 [aglImage addRepresentation:aglBitmap];
2811 [aglBitmap release];
2818 @implementation NSData (PluginExtras)
2820 - (BOOL)_web_startsWithBlankLine
2822 return [self length] > 0 && ((const char *)[self bytes])[0] == '\n';
2826 - (WebNSInteger)_web_locationAfterFirstBlankLine
2828 const char *bytes = (const char *)[self bytes];
2829 unsigned length = [self length];
2832 for (i = 0; i < length - 4; i++) {
2834 // Support for Acrobat. It sends "\n\n".
2835 if (bytes[i] == '\n' && bytes[i+1] == '\n') {
2839 // Returns the position after 2 CRLF's or 1 CRLF if it is the first line.
2840 if (bytes[i] == '\r' && bytes[i+1] == '\n') {
2844 } else if (bytes[i] == '\n') {
2845 // Support for Director. It sends "\r\n\n" (3880387).
2847 } else if (bytes[i] == '\r' && bytes[i+1] == '\n') {
2848 // Support for Flash. It sends "\r\n\r\n" (3758113).