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 "WebBaseNetscapePluginView.h"
31 #import "WebDataSourceInternal.h"
32 #import "WebDefaultUIDelegate.h"
33 #import "WebFrameBridge.h"
34 #import "WebFrameInternal.h"
35 #import "WebFrameView.h"
36 #import "WebGraphicsExtras.h"
37 #import "WebKitLogging.h"
38 #import "WebKitNSStringExtras.h"
39 #import "WebNSDataExtras.h"
40 #import "WebNSDictionaryExtras.h"
41 #import "WebNSObjectExtras.h"
42 #import "WebNSURLExtras.h"
43 #import "WebNSURLRequestExtras.h"
44 #import "WebNSViewExtras.h"
45 #import "WebNetscapePluginPackage.h"
46 #import "WebNetscapePluginStream.h"
47 #import "WebNullPluginView.h"
48 #import "WebPreferences.h"
49 #import "WebViewInternal.h"
50 #import <Carbon/Carbon.h>
51 #import <JavaScriptCore/Assertions.h>
52 #import <JavaScriptCore/npruntime_impl.h>
53 #import <WebCore/FrameLoader.h>
54 #import <WebCore/FrameMac.h>
55 #import <WebCore/FrameTree.h>
56 #import <WebCore/Page.h>
57 #import <WebKit/DOMPrivate.h>
58 #import <WebKit/WebUIDelegate.h>
59 #import <WebKitSystemInterface.h>
60 #import <objc/objc-runtime.h>
62 using namespace WebCore;
64 // Send null events 50 times a second when active, so plug-ins like Flash get high frame rates.
65 #define NullEventIntervalActive 0.02
66 #define NullEventIntervalNotActive 0.25
68 #define LoginWindowDidSwitchFromUserNotification @"WebLoginWindowDidSwitchFromUserNotification"
69 #define LoginWindowDidSwitchToUserNotification @"WebLoginWindowDidSwitchToUserNotification"
71 @interface WebBaseNetscapePluginView (Internal)
72 - (void)_viewHasMoved;
73 - (NSBitmapImageRep *)_printedPluginBitmap;
74 - (BOOL)_createAGLContextIfNeeded;
75 - (BOOL)_createWindowedAGLContext;
76 - (BOOL)_createWindowlessAGLContext;
77 - (CGLContextObj)_cglContext;
78 - (BOOL)_getAGLOffscreenBuffer:(GLvoid **)outBuffer width:(GLsizei *)outWidth height:(GLsizei *)outHeight;
79 - (void)_destroyAGLContext;
80 - (void)_reshapeAGLWindow;
81 - (void)_hideAGLWindow;
82 - (NSImage *)_aglOffscreenImageForDrawingInRect:(NSRect)drawingInRect;
85 static WebBaseNetscapePluginView *currentPluginView = nil;
87 typedef struct OpaquePortState* PortState;
89 #ifndef NP_NO_QUICKDRAW
91 // QuickDraw is not available in 64-bit
96 RgnHandle oldClipRegion;
97 RgnHandle oldVisibleRegion;
102 #endif /* NP_NO_QUICKDRAW */
105 CGContextRef context;
109 AGLContext oldContext;
112 @interface WebPluginRequest : NSObject
114 NSURLRequest *_request;
115 NSString *_frameName;
117 BOOL _didStartFromUserGesture;
118 BOOL _sendNotification;
121 - (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture;
123 - (NSURLRequest *)request;
124 - (NSString *)frameName;
125 - (void *)notifyData;
126 - (BOOL)isCurrentEventUserGesture;
127 - (BOOL)sendNotification;
131 @interface NSData (WebPluginDataExtras)
132 - (BOOL)_web_startsWithBlankLine;
133 - (WebNSInteger)_web_locationAfterFirstBlankLine;
136 static OSStatus TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *pluginView);
138 @interface WebBaseNetscapePluginView (ForwardDeclarations)
139 - (void)setWindowIfNecessary;
142 @implementation WebBaseNetscapePluginView
146 WKSendUserChangeNotifications();
151 + (void)getCarbonEvent:(EventRecord *)carbonEvent
153 carbonEvent->what = nullEvent;
154 carbonEvent->message = 0;
155 carbonEvent->when = TickCount();
156 GetGlobalMouse(&carbonEvent->where);
157 carbonEvent->where.h = static_cast<short>(carbonEvent->where.h * HIGetScaleFactor());
158 carbonEvent->where.v = static_cast<short>(carbonEvent->where.v * HIGetScaleFactor());
159 carbonEvent->modifiers = GetCurrentKeyModifiers();
161 carbonEvent->modifiers |= btnState;
164 - (void)getCarbonEvent:(EventRecord *)carbonEvent
166 [[self class] getCarbonEvent:carbonEvent];
169 - (EventModifiers)modifiersForEvent:(NSEvent *)event
171 EventModifiers modifiers;
172 unsigned int modifierFlags = [event modifierFlags];
173 NSEventType eventType = [event type];
177 if (eventType != NSLeftMouseDown && eventType != NSRightMouseDown)
178 modifiers |= btnState;
180 if (modifierFlags & NSCommandKeyMask)
183 if (modifierFlags & NSShiftKeyMask)
184 modifiers |= shiftKey;
186 if (modifierFlags & NSAlphaShiftKeyMask)
187 modifiers |= alphaLock;
189 if (modifierFlags & NSAlternateKeyMask)
190 modifiers |= optionKey;
192 if (modifierFlags & NSControlKeyMask || eventType == NSRightMouseDown)
193 modifiers |= controlKey;
198 - (void)getCarbonEvent:(EventRecord *)carbonEvent withEvent:(NSEvent *)cocoaEvent
200 if (WKConvertNSEventToCarbonEvent(carbonEvent, cocoaEvent)) {
201 carbonEvent->where.h = static_cast<short>(carbonEvent->where.h * HIGetScaleFactor());
202 carbonEvent->where.v = static_cast<short>(carbonEvent->where.v * HIGetScaleFactor());
206 NSPoint where = [[cocoaEvent window] convertBaseToScreen:[cocoaEvent locationInWindow]];
208 carbonEvent->what = nullEvent;
209 carbonEvent->message = 0;
210 carbonEvent->when = (UInt32)([cocoaEvent timestamp] * 60); // seconds to ticks
211 carbonEvent->where.h = (short)where.x;
212 carbonEvent->where.v = (short)(NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - where.y);
213 carbonEvent->modifiers = [self modifiersForEvent:cocoaEvent];
216 - (BOOL)superviewsHaveSuperviews
218 NSView *contentView = [[self window] contentView];
220 for (view = self; view != nil; view = [view superview]) {
221 if (view == contentView) {
228 #ifndef NP_NO_QUICKDRAW
229 // The WindowRef created by -[NSWindow windowRef] has a QuickDraw GrafPort that covers
230 // the entire window frame (or structure region to use the Carbon term) rather then just the window content.
231 // We can remove this when <rdar://problem/4201099> is fixed.
232 - (void)fixWindowPort
234 ASSERT(drawingModel == NPDrawingModelQuickDraw);
236 NSWindow *currentWindow = [self currentWindow];
237 if ([currentWindow isKindOfClass:objc_getClass("NSCarbonWindow")])
240 float windowHeight = [currentWindow frame].size.height;
241 NSView *contentView = [currentWindow contentView];
242 NSRect contentRect = [contentView convertRect:[contentView frame] toView:nil]; // convert to window-relative coordinates
246 SetPort(GetWindowPort((WindowRef)[currentWindow windowRef]));
248 MovePortTo(static_cast<short>(contentRect.origin.x), /* Flip Y */ static_cast<short>(windowHeight - NSMaxY(contentRect)));
249 PortSize(static_cast<short>(contentRect.size.width), static_cast<short>(contentRect.size.height));
255 - (PortState)saveAndSetNewPortStateForUpdate:(BOOL)forUpdate
257 ASSERT([self currentWindow] != nil);
259 #ifndef NP_NO_QUICKDRAW
260 // If drawing with QuickDraw, fix the window port so that it has the same bounds as the NSWindow's
261 // content view. This makes it easier to convert between AppKit view and QuickDraw port coordinates.
262 if (drawingModel == NPDrawingModelQuickDraw)
263 [self fixWindowPort];
266 WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
269 // Use AppKit to convert view coordinates to NSWindow coordinates.
270 NSRect boundsInWindow = [self convertRect:[self bounds] toView:nil];
271 NSRect visibleRectInWindow = [self convertRect:[self visibleRect] toView:nil];
273 // Flip Y to convert NSWindow coordinates to top-left-based window coordinates.
274 float borderViewHeight = [[self currentWindow] frame].size.height;
275 boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow);
276 visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow);
278 #ifndef NP_NO_QUICKDRAW
279 // Look at the Carbon port to convert top-left-based window coordinates into top-left-based content coordinates.
280 if (drawingModel == NPDrawingModelQuickDraw) {
282 CGrafPtr port = GetWindowPort(windowRef);
283 GetPortBounds(port, &portBounds);
285 PixMap *pix = *GetPortPixMap(port);
286 boundsInWindow.origin.x += pix->bounds.left - portBounds.left;
287 boundsInWindow.origin.y += pix->bounds.top - portBounds.top;
288 visibleRectInWindow.origin.x += pix->bounds.left - portBounds.left;
289 visibleRectInWindow.origin.y += pix->bounds.top - portBounds.top;
293 window.x = (int32)boundsInWindow.origin.x;
294 window.y = (int32)boundsInWindow.origin.y;
295 window.width = static_cast<uint32>(NSWidth(boundsInWindow));
296 window.height = static_cast<uint32>(NSHeight(boundsInWindow));
298 // "Clip-out" the plug-in when:
299 // 1) it's not really in a window or off-screen or has no height or width.
300 // 2) window.x is a "big negative number" which is how WebCore expresses off-screen widgets.
301 // 3) the window is miniaturized or the app is hidden
302 // 4) we're inside of viewWillMoveToWindow: with a nil window. In this case, superviews may already have nil
303 // superviews and nil windows and results from convertRect:toView: are incorrect.
304 NSWindow *realWindow = [self window];
305 if (window.width <= 0 || window.height <= 0 || window.x < -100000
306 || realWindow == nil || [realWindow isMiniaturized]
308 || ![self superviewsHaveSuperviews]
309 || [self isHiddenOrHasHiddenAncestor]) {
310 // The following code tries to give plug-ins the same size they will eventually have.
311 // The specifiedWidth and specifiedHeight variables are used to predict the size that
312 // WebCore will eventually resize us to.
314 // The QuickTime plug-in has problems if you give it a width or height of 0.
315 // Since other plug-ins also might have the same sort of trouble, we make sure
316 // to always give plug-ins a size other than 0,0.
318 if (window.width <= 0) {
319 window.width = specifiedWidth > 0 ? specifiedWidth : 100;
321 if (window.height <= 0) {
322 window.height = specifiedHeight > 0 ? specifiedHeight : 100;
325 window.clipRect.bottom = window.clipRect.top;
326 window.clipRect.left = window.clipRect.right;
328 window.clipRect.top = (uint16)visibleRectInWindow.origin.y;
329 window.clipRect.left = (uint16)visibleRectInWindow.origin.x;
330 window.clipRect.bottom = (uint16)(visibleRectInWindow.origin.y + visibleRectInWindow.size.height);
331 window.clipRect.right = (uint16)(visibleRectInWindow.origin.x + visibleRectInWindow.size.width);
334 // Save the port state, set up the port for entry into the plugin
336 switch (drawingModel) {
337 #ifndef NP_NO_QUICKDRAW
338 case NPDrawingModelQuickDraw:
342 CGrafPtr port = GetWindowPort(windowRef);
343 GetPortBounds(port, &portBounds);
344 nPort.qdPort.port = port;
345 nPort.qdPort.portx = (int32)-boundsInWindow.origin.x;
346 nPort.qdPort.porty = (int32)-boundsInWindow.origin.y;
347 window.window = &nPort;
349 PortState_QD *qdPortState = (PortState_QD*)malloc(sizeof(PortState_QD));
350 portState = (PortState)qdPortState;
352 GetPort(&qdPortState->oldPort);
354 qdPortState->oldOrigin.h = portBounds.left;
355 qdPortState->oldOrigin.v = portBounds.top;
357 qdPortState->oldClipRegion = NewRgn();
358 GetPortClipRegion(port, qdPortState->oldClipRegion);
360 qdPortState->oldVisibleRegion = NewRgn();
361 GetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
363 RgnHandle clipRegion = NewRgn();
364 qdPortState->clipRegion = clipRegion;
366 MacSetRectRgn(clipRegion,
367 window.clipRect.left + nPort.qdPort.portx, window.clipRect.top + nPort.qdPort.porty,
368 window.clipRect.right + nPort.qdPort.portx, window.clipRect.bottom + nPort.qdPort.porty);
370 // Clip to dirty region so plug-in does not draw over already-drawn regions of the window that are
371 // not going to be redrawn this update. This forces plug-ins to play nice with z-index ordering.
373 RgnHandle viewClipRegion = NewRgn();
375 // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
376 // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
377 // knows about the true set of dirty rects.
378 NSView *opaqueAncestor = [self opaqueAncestor];
379 const NSRect *dirtyRects;
380 int dirtyRectCount, dirtyRectIndex;
381 [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount];
383 for (dirtyRectIndex = 0; dirtyRectIndex < dirtyRectCount; dirtyRectIndex++) {
384 NSRect dirtyRect = [self convertRect:dirtyRects[dirtyRectIndex] fromView:opaqueAncestor];
385 if (!NSEqualSizes(dirtyRect.size, NSZeroSize)) {
386 // Create a region for this dirty rect
387 RgnHandle dirtyRectRegion = NewRgn();
388 SetRectRgn(dirtyRectRegion, static_cast<short>(NSMinX(dirtyRect)), static_cast<short>(NSMinY(dirtyRect)), static_cast<short>(NSMaxX(dirtyRect)), static_cast<short>(NSMaxY(dirtyRect)));
390 // Union this dirty rect with the rest of the dirty rects
391 UnionRgn(viewClipRegion, dirtyRectRegion, viewClipRegion);
392 DisposeRgn(dirtyRectRegion);
396 // Intersect the dirty region with the clip region, so that we only draw over dirty parts
397 SectRgn(clipRegion, viewClipRegion, clipRegion);
398 DisposeRgn(viewClipRegion);
401 qdPortState->forUpdate = forUpdate;
403 // Switch to the port and set it up.
407 ForeColor(blackColor);
408 BackColor(whiteColor);
410 SetOrigin(nPort.qdPort.portx, nPort.qdPort.porty);
412 SetPortClipRegion(nPort.qdPort.port, clipRegion);
415 // AppKit may have tried to help us by doing a BeginUpdate.
416 // But the invalid region at that level didn't include AppKit's notion of what was not valid.
417 // We reset the port's visible region to counteract what BeginUpdate did.
418 SetPortVisibleRegion(nPort.qdPort.port, clipRegion);
420 // Some plugins do their own BeginUpdate/EndUpdate.
421 // For those, we must make sure that the update region contains the area we want to draw.
422 InvalWindowRgn(windowRef, clipRegion);
426 #endif /* NP_NO_QUICKDRAW */
428 case NPDrawingModelCoreGraphics:
430 // A CoreGraphics plugin's window may only be set while the plugin view is being updated
431 ASSERT(forUpdate && [NSView focusView] == self);
433 PortState_CG *cgPortState = (PortState_CG *)malloc(sizeof(PortState_CG));
434 portState = (PortState)cgPortState;
435 cgPortState->context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
437 // Update the plugin's window/context
438 nPort.cgPort.window = windowRef;
439 nPort.cgPort.context = cgPortState->context;
440 window.window = &nPort.cgPort;
442 // Save current graphics context's state; will be restored by -restorePortState:
443 CGContextSaveGState(nPort.cgPort.context);
445 // FIXME (4544971): Clip to dirty region when updating in "windowless" mode (transparent), like in the QD case
449 case NPDrawingModelOpenGL:
451 // An OpenGL plugin's window may only be set while the plugin view is being updated
452 ASSERT(forUpdate && [NSView focusView] == self);
454 // Clear the "current" window and context -- they will be assigned below (if all goes well)
455 nPort.aglPort.window = NULL;
456 nPort.aglPort.context = NULL;
458 // Create AGL context if needed
459 if (![self _createAGLContextIfNeeded]) {
460 LOG_ERROR("Could not create AGL context");
464 // Update the plugin's window/context
465 nPort.aglPort.window = windowRef;
466 nPort.aglPort.context = [self _cglContext];
467 window.window = &nPort.aglPort;
469 // Save/set current AGL context
470 PortState_GL *glPortState = (PortState_GL *)malloc(sizeof(PortState_GL));
471 portState = (PortState)glPortState;
472 glPortState->oldContext = aglGetCurrentContext();
473 aglSetCurrentContext(aglContext);
475 // Adjust viewport according to clip
476 switch (window.type) {
477 case NPWindowTypeWindow:
478 glViewport(static_cast<GLint>(NSMinX(boundsInWindow) - NSMinX(visibleRectInWindow)), static_cast<GLint>(NSMaxY(visibleRectInWindow) - NSMaxY(boundsInWindow)), window.width, window.height);
481 case NPWindowTypeDrawable:
483 GLsizei width, height;
484 if ([self _getAGLOffscreenBuffer:NULL width:&width height:&height])
485 glViewport(0, 0, width, height);
490 ASSERT_NOT_REACHED();
497 ASSERT_NOT_REACHED();
505 - (PortState)saveAndSetNewPortState
507 return [self saveAndSetNewPortStateForUpdate:NO];
510 - (void)restorePortState:(PortState)portState
512 ASSERT([self currentWindow]);
515 switch (drawingModel) {
516 #ifndef NP_NO_QUICKDRAW
517 case NPDrawingModelQuickDraw:
519 PortState_QD *qdPortState = (PortState_QD *)portState;
520 WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
521 CGrafPtr port = GetWindowPort(windowRef);
522 if (qdPortState->forUpdate)
523 ValidWindowRgn(windowRef, qdPortState->clipRegion);
525 SetOrigin(qdPortState->oldOrigin.h, qdPortState->oldOrigin.v);
527 SetPortClipRegion(port, qdPortState->oldClipRegion);
528 if (qdPortState->forUpdate)
529 SetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
531 DisposeRgn(qdPortState->oldClipRegion);
532 DisposeRgn(qdPortState->oldVisibleRegion);
533 DisposeRgn(qdPortState->clipRegion);
535 SetPort(qdPortState->oldPort);
538 #endif /* NP_NO_QUICKDRAW */
540 case NPDrawingModelCoreGraphics:
542 ASSERT([NSView focusView] == self);
543 ASSERT(((PortState_CG *)portState)->context == nPort.cgPort.context);
544 CGContextRestoreGState(nPort.cgPort.context);
548 case NPDrawingModelOpenGL:
549 aglSetCurrentContext(((PortState_GL *)portState)->oldContext);
553 ASSERT_NOT_REACHED();
558 - (BOOL)sendEvent:(EventRecord *)event
564 // If at any point the user clicks or presses a key from within a plugin, set the
565 // currentEventIsUserGesture flag to true. This is important to differentiate legitimate
566 // window.open() calls; we still want to allow those. See rdar://problem/4010765
567 if (event->what == mouseDown || event->what == keyDown || event->what == mouseUp || event->what == autoKey)
568 currentEventIsUserGesture = YES;
570 suspendKeyUpEvents = NO;
575 ASSERT(NPP_HandleEvent);
577 // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
578 // We probably don't want more general reentrancy protection; we are really
579 // protecting only against this one case, which actually comes up when
580 // you first install the SVG viewer plug-in.
584 Page* page = core([self webFrame])->page();
585 bool wasDeferring = page->defersLoading();
587 page->setDefersLoading(true);
589 // Can only send updateEvt to CoreGraphics and OpenGL plugins when actually drawing
590 ASSERT((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || event->what != updateEvt || [NSView focusView] == self);
592 BOOL updating = event->what == updateEvt;
594 if ((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || event->what == updateEvt) {
595 // 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
596 // allowed to draw at any other time.
597 portState = [self saveAndSetNewPortStateForUpdate:updating];
599 // We may have changed the window, so inform the plug-in.
600 [self setWindowIfNecessary];
604 #if !defined(NDEBUG) && !defined(NP_NO_QUICKDRAW)
605 // Draw green to help debug.
606 // If we see any green we know something's wrong.
607 // Note that PaintRect() only works for QuickDraw plugins; otherwise the current QD port is undefined.
608 if (drawingModel == NPDrawingModelQuickDraw && !isTransparent && event->what == updateEvt) {
609 ForeColor(greenColor);
610 const Rect bigRect = { -10000, -10000, 10000, 10000 };
612 ForeColor(blackColor);
616 // Temporarily retain self in case the plug-in view is released while sending an event.
617 [[self retain] autorelease];
619 [self willCallPlugInFunction];
620 BOOL acceptedEvent = NPP_HandleEvent(instance, event);
621 [self didCallPlugInFunction];
623 currentEventIsUserGesture = NO;
626 if ([self currentWindow])
627 [self restorePortState:portState];
632 page->setDefersLoading(false);
634 return acceptedEvent;
637 - (void)sendActivateEvent:(BOOL)activate
641 [self getCarbonEvent:&event];
642 event.what = activateEvt;
643 WindowRef windowRef = (WindowRef)[[self window] windowRef];
644 event.message = (unsigned long)windowRef;
646 event.modifiers |= activeFlag;
650 acceptedEvent = [self sendEvent:&event];
652 LOG(PluginEvents, "NPP_HandleEvent(activateEvent): %d isActive: %d", acceptedEvent, activate);
655 - (BOOL)sendUpdateEvent
659 [self getCarbonEvent:&event];
660 event.what = updateEvt;
661 WindowRef windowRef = (WindowRef)[[self window] windowRef];
662 event.message = (unsigned long)windowRef;
664 BOOL acceptedEvent = [self sendEvent:&event];
666 LOG(PluginEvents, "NPP_HandleEvent(updateEvt): %d", acceptedEvent);
668 return acceptedEvent;
675 [self getCarbonEvent:&event];
677 // Plug-in should not react to cursor position when not active or when a menu is down.
678 MenuTrackingData trackingData;
679 OSStatus error = GetMenuTrackingData(NULL, &trackingData);
681 // Plug-in should not react to cursor position when the actual window is not key.
682 if (![[self window] isKeyWindow] || (error == noErr && trackingData.menu)) {
683 // FIXME: Does passing a v and h of -1 really prevent it from reacting to the cursor position?
688 [self sendEvent:&event];
691 - (void)stopNullEvents
693 [nullEventTimer invalidate];
694 [nullEventTimer release];
695 nullEventTimer = nil;
698 - (void)restartNullEvents
700 ASSERT([self window]);
703 [self stopNullEvents];
705 if (!isStarted || [[self window] isMiniaturized])
708 NSTimeInterval interval;
710 // If the plugin is completely obscured (scrolled out of view, for example), then we will
711 // send null events at a reduced rate.
712 interval = !isCompletelyObscured ? NullEventIntervalActive : NullEventIntervalNotActive;
713 nullEventTimer = [[NSTimer scheduledTimerWithTimeInterval:interval
715 selector:@selector(sendNullEvent)
717 repeats:YES] retain];
720 - (BOOL)acceptsFirstResponder
725 - (void)installKeyEventHandler
727 static const EventTypeSpec sTSMEvents[] =
729 { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
732 if (!keyEventHandler) {
733 InstallEventHandler(GetWindowEventTarget((WindowRef)[[self window] windowRef]),
734 NewEventHandlerUPP(TSMEventHandler),
735 GetEventTypeCount(sTSMEvents),
742 - (void)removeKeyEventHandler
744 if (keyEventHandler) {
745 RemoveEventHandler(keyEventHandler);
746 keyEventHandler = NULL;
750 - (void)setHasFocus:(BOOL)flag
752 if (hasFocus != flag) {
755 [self getCarbonEvent:&event];
758 event.what = getFocusEvent;
759 acceptedEvent = [self sendEvent:&event];
760 LOG(PluginEvents, "NPP_HandleEvent(getFocusEvent): %d", acceptedEvent);
761 [self installKeyEventHandler];
763 event.what = loseFocusEvent;
764 acceptedEvent = [self sendEvent:&event];
765 LOG(PluginEvents, "NPP_HandleEvent(loseFocusEvent): %d", acceptedEvent);
766 [self removeKeyEventHandler];
771 - (BOOL)becomeFirstResponder
773 [self setHasFocus:YES];
777 - (BOOL)resignFirstResponder
779 [self setHasFocus:NO];
783 // AppKit doesn't call mouseDown or mouseUp on right-click. Simulate control-click
784 // mouseDown and mouseUp so plug-ins get the right-click event as they do in Carbon (3125743).
785 - (void)rightMouseDown:(NSEvent *)theEvent
787 [self mouseDown:theEvent];
790 - (void)rightMouseUp:(NSEvent *)theEvent
792 [self mouseUp:theEvent];
795 - (void)mouseDown:(NSEvent *)theEvent
799 [self getCarbonEvent:&event withEvent:theEvent];
800 event.what = mouseDown;
803 acceptedEvent = [self sendEvent:&event];
805 LOG(PluginEvents, "NPP_HandleEvent(mouseDown): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
808 - (void)mouseUp:(NSEvent *)theEvent
812 [self getCarbonEvent:&event withEvent:theEvent];
813 event.what = mouseUp;
816 acceptedEvent = [self sendEvent:&event];
818 LOG(PluginEvents, "NPP_HandleEvent(mouseUp): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
821 - (void)mouseEntered:(NSEvent *)theEvent
825 [self getCarbonEvent:&event withEvent:theEvent];
826 event.what = adjustCursorEvent;
829 acceptedEvent = [self sendEvent:&event];
831 LOG(PluginEvents, "NPP_HandleEvent(mouseEntered): %d", acceptedEvent);
834 - (void)mouseExited:(NSEvent *)theEvent
838 [self getCarbonEvent:&event withEvent:theEvent];
839 event.what = adjustCursorEvent;
842 acceptedEvent = [self sendEvent:&event];
844 LOG(PluginEvents, "NPP_HandleEvent(mouseExited): %d", acceptedEvent);
846 // Set cursor back to arrow cursor.
847 [[NSCursor arrowCursor] set];
850 - (void)mouseDragged:(NSEvent *)theEvent
852 // Do nothing so that other responders don't respond to the drag that initiated in this view.
855 - (UInt32)keyMessageForEvent:(NSEvent *)event
857 NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())];
862 [data getBytes:&characterCode length:1];
863 UInt16 keyCode = [event keyCode];
864 return keyCode << 8 | characterCode;
867 - (void)keyUp:(NSEvent *)theEvent
869 WKSendKeyEventToTSM(theEvent);
871 // TSM won't send keyUp events so we have to send them ourselves.
872 // Only send keyUp events after we receive the TSM callback because this is what plug-in expect from OS 9.
873 if (!suspendKeyUpEvents) {
876 [self getCarbonEvent:&event withEvent:theEvent];
879 if (event.message == 0) {
880 event.message = [self keyMessageForEvent:theEvent];
883 [self sendEvent:&event];
887 - (void)keyDown:(NSEvent *)theEvent
889 suspendKeyUpEvents = YES;
890 WKSendKeyEventToTSM(theEvent);
893 static OSStatus TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *pluginView)
895 EventRef rawKeyEventRef;
896 OSStatus status = GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(EventRef), NULL, &rawKeyEventRef);
897 if (status != noErr) {
898 LOG_ERROR("GetEventParameter failed with error: %d", status);
902 // Two-pass read to allocate/extract Mac charCodes
904 status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL);
905 if (status != noErr) {
906 LOG_ERROR("GetEventParameter failed with error: %d", status);
909 char *buffer = (char *)malloc(numBytes);
910 status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, numBytes, NULL, buffer);
911 if (status != noErr) {
912 LOG_ERROR("GetEventParameter failed with error: %d", status);
917 EventRef cloneEvent = CopyEvent(rawKeyEventRef);
919 for (i = 0; i < numBytes; i++) {
920 status = SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes, typeChar, 1 /* one char code */, &buffer[i]);
921 if (status != noErr) {
922 LOG_ERROR("SetEventParameter failed with error: %d", status);
927 EventRecord eventRec;
928 if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
930 acceptedEvent = [(WebBaseNetscapePluginView *)pluginView sendEvent:&eventRec];
932 LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu",
933 acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask));
935 // We originally thought that if the plug-in didn't accept this event,
936 // we should pass it along so that keyboard scrolling, for example, will work.
937 // In practice, this is not a good idea, because plug-ins tend to eat the event but return false.
938 // MacIE handles each key event twice because of this, but we will emulate the other browsers instead.
941 ReleaseEvent(cloneEvent);
947 // Fake up command-modified events so cut, copy, paste and select all menus work.
948 - (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character
951 [self getCarbonEvent:&event];
952 event.what = keyDown;
953 event.modifiers |= cmdKey;
954 event.message = keyCode << 8 | character;
955 [self sendEvent:&event];
958 - (void)cut:(id)sender
960 [self sendModifierEventWithKeyCode:7 character:'x'];
963 - (void)copy:(id)sender
965 [self sendModifierEventWithKeyCode:8 character:'c'];
968 - (void)paste:(id)sender
970 [self sendModifierEventWithKeyCode:9 character:'v'];
973 - (void)selectAll:(id)sender
975 [self sendModifierEventWithKeyCode:0 character:'a'];
978 #pragma mark WEB_NETSCAPE_PLUGIN
980 - (BOOL)isNewWindowEqualToOldWindow
982 if (window.x != lastSetWindow.x)
984 if (window.y != lastSetWindow.y)
986 if (window.width != lastSetWindow.width)
988 if (window.height != lastSetWindow.height)
990 if (window.clipRect.top != lastSetWindow.clipRect.top)
992 if (window.clipRect.left != lastSetWindow.clipRect.left)
994 if (window.clipRect.bottom != lastSetWindow.clipRect.bottom)
996 if (window.clipRect.right != lastSetWindow.clipRect.right)
998 if (window.type != lastSetWindow.type)
1001 switch (drawingModel) {
1002 #ifndef NP_NO_QUICKDRAW
1003 case NPDrawingModelQuickDraw:
1004 if (nPort.qdPort.portx != lastSetPort.qdPort.portx)
1006 if (nPort.qdPort.porty != lastSetPort.qdPort.porty)
1008 if (nPort.qdPort.port != lastSetPort.qdPort.port)
1011 #endif /* NP_NO_QUICKDRAW */
1013 case NPDrawingModelCoreGraphics:
1014 if (nPort.cgPort.window != lastSetPort.cgPort.window)
1016 if (nPort.cgPort.context != lastSetPort.cgPort.context)
1020 case NPDrawingModelOpenGL:
1021 if (nPort.aglPort.window != lastSetPort.aglPort.window)
1023 if (nPort.aglPort.context != lastSetPort.aglPort.context)
1028 ASSERT_NOT_REACHED();
1035 - (void)updateAndSetWindow
1037 if (drawingModel == NPDrawingModelCoreGraphics || drawingModel == NPDrawingModelOpenGL) {
1038 // Can only update CoreGraphics and OpenGL plugins while redrawing the plugin view
1039 [self setNeedsDisplay:YES];
1043 // Can't update the plugin if it has not started (or has been stopped)
1047 PortState portState = [self saveAndSetNewPortState];
1049 [self setWindowIfNecessary];
1050 [self restorePortState:portState];
1055 - (void)setWindowIfNecessary
1061 if (![self isNewWindowEqualToOldWindow]) {
1062 // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
1063 // We probably don't want more general reentrancy protection; we are really
1064 // protecting only against this one case, which actually comes up when
1065 // you first install the SVG viewer plug-in.
1067 ASSERT(!inSetWindow);
1071 // A CoreGraphics or OpenGL plugin's window may only be set while the plugin is being updated
1072 ASSERT((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || [NSView focusView] == self);
1074 [self willCallPlugInFunction];
1075 npErr = NPP_SetWindow(instance, &window);
1076 [self didCallPlugInFunction];
1080 switch (drawingModel) {
1081 #ifndef NP_NO_QUICKDRAW
1082 case NPDrawingModelQuickDraw:
1083 LOG(Plugins, "NPP_SetWindow (QuickDraw): %d, port=0x%08x, window.x:%d window.y:%d window.width:%d window.height:%d",
1084 npErr, (int)nPort.qdPort.port, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1086 #endif /* NP_NO_QUICKDRAW */
1088 case NPDrawingModelCoreGraphics:
1089 LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d",
1090 npErr, nPort.cgPort.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1093 case NPDrawingModelOpenGL:
1094 LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d",
1095 npErr, nPort.aglPort.window, nPort.aglPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1099 ASSERT_NOT_REACHED();
1102 #endif /* !defined(NDEBUG) */
1104 lastSetWindow = window;
1105 lastSetPort = nPort;
1109 - (void)removeTrackingRect
1112 [self removeTrackingRect:trackingTag];
1115 // Must release the window to balance the retain in resetTrackingRect.
1116 // But must do it after setting trackingTag to 0 so we don't re-enter.
1117 [[self window] release];
1121 - (void)resetTrackingRect
1123 [self removeTrackingRect];
1125 // Must retain the window so that removeTrackingRect can work after the window is closed.
1126 [[self window] retain];
1127 trackingTag = [self addTrackingRect:[self bounds] owner:self userData:nil assumeInside:NO];
1131 + (void)setCurrentPluginView:(WebBaseNetscapePluginView *)view
1133 currentPluginView = view;
1136 + (WebBaseNetscapePluginView *)currentPluginView
1138 return currentPluginView;
1148 // Do nothing. Overridden by subclasses.
1151 - (void)addWindowObservers
1153 ASSERT([self window]);
1155 NSWindow *theWindow = [self window];
1157 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
1158 [notificationCenter addObserver:self selector:@selector(windowWillClose:)
1159 name:NSWindowWillCloseNotification object:theWindow];
1160 [notificationCenter addObserver:self selector:@selector(windowBecameKey:)
1161 name:NSWindowDidBecomeKeyNotification object:theWindow];
1162 [notificationCenter addObserver:self selector:@selector(windowResignedKey:)
1163 name:NSWindowDidResignKeyNotification object:theWindow];
1164 [notificationCenter addObserver:self selector:@selector(windowDidMiniaturize:)
1165 name:NSWindowDidMiniaturizeNotification object:theWindow];
1166 [notificationCenter addObserver:self selector:@selector(windowDidDeminiaturize:)
1167 name:NSWindowDidDeminiaturizeNotification object:theWindow];
1169 [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchFromUser:)
1170 name:LoginWindowDidSwitchFromUserNotification object:nil];
1171 [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchToUser:)
1172 name:LoginWindowDidSwitchToUserNotification object:nil];
1175 - (void)removeWindowObservers
1177 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
1178 [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:nil];
1179 [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
1180 [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
1181 [notificationCenter removeObserver:self name:NSWindowDidMiniaturizeNotification object:nil];
1182 [notificationCenter removeObserver:self name:NSWindowDidDeminiaturizeNotification object:nil];
1183 [notificationCenter removeObserver:self name:LoginWindowDidSwitchFromUserNotification object:nil];
1184 [notificationCenter removeObserver:self name:LoginWindowDidSwitchToUserNotification object:nil];
1189 ASSERT([self currentWindow]);
1195 if (![self canStart]) {
1199 ASSERT([self webView]);
1201 if (![[[self webView] preferences] arePlugInsEnabled]) {
1207 // Open the plug-in package so it remains loaded while this instance uses it
1210 // Initialize drawingModel to an invalid value so that we can detect when the plugin does not specify a drawingModel
1211 drawingModel = (NPDrawingModel)-1;
1213 // Plug-ins are "windowed" by default. On MacOS, windowed plug-ins share the same window and graphics port as the main
1214 // browser window. Windowless plug-ins are rendered off-screen, then copied into the main browser window.
1215 window.type = NPWindowTypeWindow;
1217 // NPN_New(), which creates the plug-in instance, should never be called while calling a plug-in function for that instance.
1218 ASSERT(pluginFunctionCallDepth == 0);
1220 [[self class] setCurrentPluginView:self];
1221 NPError npErr = NPP_New((char *)[MIMEType cString], instance, mode, argsCount, cAttributes, cValues, NULL);
1222 [[self class] setCurrentPluginView:nil];
1224 if (drawingModel == (NPDrawingModel)-1) {
1225 #ifndef NP_NO_QUICKDRAW
1226 // Default to QuickDraw if the plugin did not specify a drawing model.
1227 drawingModel = NPDrawingModelQuickDraw;
1229 // QuickDraw is not available, so we can't default to it. We could default to CoreGraphics instead, but
1230 // if the plugin did not specify the CoreGraphics drawing model then it must be one of the old QuickDraw
1231 // plugins. Thus, the plugin is unsupported and should not be started. Destroy it here and bail out.
1232 LOG(Plugins, "Plugin only supports QuickDraw, but QuickDraw is unavailable: %@", plugin);
1233 NPP_Destroy(instance, NULL);
1234 instance->pdata = NULL;
1240 LOG(Plugins, "NPP_New: %d", npErr);
1241 if (npErr != NPERR_NO_ERROR) {
1242 LOG_ERROR("NPP_New failed with error: %d", npErr);
1249 [self updateAndSetWindow];
1251 if ([self window]) {
1252 [self addWindowObservers];
1253 if ([[self window] isKeyWindow]) {
1254 [self sendActivateEvent:YES];
1256 [self restartNullEvents];
1259 [self resetTrackingRect];
1268 // If we're already calling a plug-in function, do not call NPP_Destroy(). The plug-in function we are calling
1269 // may assume that its instance->pdata, or other memory freed by NPP_Destroy(), is valid and unchanged until said
1270 // plugin-function returns.
1271 // See <rdar://problem/4480737>.
1272 if (pluginFunctionCallDepth > 0) {
1273 shouldStopSoon = YES;
1277 [self removeTrackingRect];
1285 // Stop any active streams
1286 [streams makeObjectsPerformSelector:@selector(stop)];
1288 // Stop the null events
1289 [self stopNullEvents];
1291 // Set cursor back to arrow cursor
1292 [[NSCursor arrowCursor] set];
1294 // Stop notifications and callbacks.
1295 [self removeWindowObservers];
1296 [[pendingFrameLoads allKeys] makeObjectsPerformSelector:@selector(_setInternalLoadDelegate:) withObject:nil];
1297 [NSObject cancelPreviousPerformRequestsWithTarget:self];
1299 // Setting the window type to 0 ensures that NPP_SetWindow will be called if the plug-in is restarted.
1300 lastSetWindow.type = (NPWindowType)0;
1303 npErr = NPP_Destroy(instance, NULL);
1304 LOG(Plugins, "NPP_Destroy: %d", npErr);
1306 instance->pdata = NULL;
1308 // This instance no longer needs the plug-in package
1311 // We usually remove the key event handler in resignFirstResponder but it is possible that resignFirstResponder
1312 // may never get called so we can't completely rely on it.
1313 [self removeKeyEventHandler];
1315 if (drawingModel == NPDrawingModelOpenGL)
1316 [self _destroyAGLContext];
1324 - (WebDataSource *)dataSource
1326 // Do nothing. Overridden by subclasses.
1330 - (WebFrame *)webFrame
1332 return [[self dataSource] webFrame];
1335 - (WebView *)webView
1337 return [[self webFrame] webView];
1340 - (NSWindow *)currentWindow
1342 return [self window] ? [self window] : [[self webView] hostWindow];
1345 - (NPP)pluginPointer
1350 - (WebNetscapePluginPackage *)plugin
1355 - (void)setPlugin:(WebNetscapePluginPackage *)thePlugin;
1361 NPP_New = [plugin NPP_New];
1362 NPP_Destroy = [plugin NPP_Destroy];
1363 NPP_SetWindow = [plugin NPP_SetWindow];
1364 NPP_NewStream = [plugin NPP_NewStream];
1365 NPP_WriteReady = [plugin NPP_WriteReady];
1366 NPP_Write = [plugin NPP_Write];
1367 NPP_StreamAsFile = [plugin NPP_StreamAsFile];
1368 NPP_DestroyStream = [plugin NPP_DestroyStream];
1369 NPP_HandleEvent = [plugin NPP_HandleEvent];
1370 NPP_URLNotify = [plugin NPP_URLNotify];
1371 NPP_GetValue = [plugin NPP_GetValue];
1372 NPP_SetValue = [plugin NPP_SetValue];
1373 NPP_Print = [plugin NPP_Print];
1376 - (void)setMIMEType:(NSString *)theMIMEType
1378 NSString *type = [theMIMEType copy];
1383 - (void)setBaseURL:(NSURL *)theBaseURL
1385 [theBaseURL retain];
1387 baseURL = theBaseURL;
1390 - (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values;
1392 ASSERT([keys count] == [values count]);
1394 // Convert the attributes to 2 C string arrays.
1395 // These arrays are passed to NPP_New, but the strings need to be
1396 // modifiable and live the entire life of the plugin.
1398 // The Java plug-in requires the first argument to be the base URL
1399 if ([MIMEType isEqualToString:@"application/x-java-applet"]) {
1400 cAttributes = (char **)malloc(([keys count] + 1) * sizeof(char *));
1401 cValues = (char **)malloc(([values count] + 1) * sizeof(char *));
1402 cAttributes[0] = strdup("DOCBASE");
1403 cValues[0] = strdup([baseURL _web_URLCString]);
1406 cAttributes = (char **)malloc([keys count] * sizeof(char *));
1407 cValues = (char **)malloc([values count] * sizeof(char *));
1410 BOOL isWMP = [[[plugin bundle] bundleIdentifier] isEqualToString:@"com.microsoft.WMP.defaultplugin"];
1413 unsigned count = [keys count];
1414 for (i = 0; i < count; i++) {
1415 NSString *key = [keys objectAtIndex:i];
1416 NSString *value = [values objectAtIndex:i];
1417 if ([key _webkit_isCaseInsensitiveEqualToString:@"height"]) {
1418 specifiedHeight = [value intValue];
1419 } else if ([key _webkit_isCaseInsensitiveEqualToString:@"width"]) {
1420 specifiedWidth = [value intValue];
1422 // Avoid Window Media Player crash when these attributes are present.
1423 if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) {
1426 cAttributes[argsCount] = strdup([key UTF8String]);
1427 cValues[argsCount] = strdup([value UTF8String]);
1428 LOG(Plugins, "%@ = %@", key, value);
1433 - (void)setMode:(int)theMode
1440 - initWithFrame:(NSRect)frame
1442 [super initWithFrame:frame];
1444 instance = &instanceStruct;
1445 instance->ndata = self;
1446 streams = [[NSMutableArray alloc] init];
1447 pendingFrameLoads = [[NSMutableDictionary alloc] init];
1452 - (void)freeAttributeKeysAndValues
1455 for (i = 0; i < argsCount; i++) {
1456 free(cAttributes[i]);
1471 [pendingFrameLoads release];
1475 ASSERT(!aglContext);
1477 [self freeAttributeKeysAndValues];
1486 [self freeAttributeKeysAndValues];
1491 - (void)drawRect:(NSRect)rect
1497 if ([NSGraphicsContext currentContextDrawingToScreen])
1498 [self sendUpdateEvent];
1500 NSBitmapImageRep *printedPluginBitmap = [self _printedPluginBitmap];
1501 if (printedPluginBitmap) {
1502 // Flip the bitmap before drawing because the QuickDraw port is flipped relative
1504 CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
1505 CGContextSaveGState(cgContext);
1506 NSRect bounds = [self bounds];
1507 CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
1508 CGContextScaleCTM(cgContext, 1.0f, -1.0f);
1509 [printedPluginBitmap drawInRect:bounds];
1510 CGContextRestoreGState(cgContext);
1514 // If this is a windowless OpenGL plugin, blit its contents back into this view. The plug-in just drew into the offscreen context.
1515 if (drawingModel == NPDrawingModelOpenGL && window.type == NPWindowTypeDrawable) {
1516 NSImage *aglOffscreenImage = [self _aglOffscreenImageForDrawingInRect:rect];
1517 if (aglOffscreenImage) {
1518 // Flip the context before drawing because the CGL context is flipped relative to this view.
1519 CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
1520 CGContextSaveGState(cgContext);
1521 NSRect bounds = [self bounds];
1522 CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
1523 CGContextScaleCTM(cgContext, 1.0f, -1.0f);
1525 // Copy 'rect' from the offscreen buffer to this view (the flip above makes this sort of tricky)
1526 NSRect flippedRect = rect;
1527 flippedRect.origin.y = NSMaxY(bounds) - NSMaxY(flippedRect);
1528 [aglOffscreenImage drawInRect:flippedRect fromRect:flippedRect operation:NSCompositeSourceOver fraction:1.0f];
1529 CGContextRestoreGState(cgContext);
1541 [super renewGState];
1543 // -renewGState is called whenever the view's geometry changes. It's a little hacky to override this method, but
1544 // much safer than walking up the view hierarchy and observing frame/bounds changed notifications, since you don't
1545 // have to track subsequent changes to the view hierarchy and add/remove notification observers.
1546 // NSOpenGLView uses the exact same technique to reshape its OpenGL surface.
1547 [self _viewHasMoved];
1550 #ifndef NP_NO_QUICKDRAW
1551 -(void)tellQuickTimeToChill
1553 ASSERT(drawingModel == NPDrawingModelQuickDraw);
1555 // Make a call to the secret QuickDraw API that makes QuickTime calm down.
1556 WindowRef windowRef = (WindowRef)[[self window] windowRef];
1560 CGrafPtr port = GetWindowPort(windowRef);
1562 GetPortBounds(port, &bounds);
1563 WKCallDrawingNotification(port, &bounds);
1565 #endif /* NP_NO_QUICKDRAW */
1567 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
1569 #ifndef NP_NO_QUICKDRAW
1570 if (drawingModel == NPDrawingModelQuickDraw)
1571 [self tellQuickTimeToChill];
1574 // We must remove the tracking rect before we move to the new window.
1575 // Once we move to the new window, it will be too late.
1576 [self removeTrackingRect];
1577 [self removeWindowObservers];
1579 // Workaround for: <rdar://problem/3822871> resignFirstResponder is not sent to first responder view when it is removed from the window
1580 [self setHasFocus:NO];
1583 // Hide the AGL child window
1584 if (drawingModel == NPDrawingModelOpenGL)
1585 [self _hideAGLWindow];
1587 if ([[self webView] hostWindow]) {
1588 // View will be moved out of the actual window but it still has a host window.
1589 [self stopNullEvents];
1591 // View will have no associated windows.
1594 // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
1595 // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
1596 [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1601 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
1603 if (!newSuperview) {
1604 // Stop the plug-in when it is removed from its superview. It is not sufficient to do this in -viewWillMoveToWindow:nil, because
1605 // the WebView might still has a hostWindow at that point, which prevents the plug-in from being destroyed.
1606 // There is no need to start the plug-in when moving into a superview. -viewDidMoveToWindow takes care of that.
1609 // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
1610 // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
1611 [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1615 - (void)viewDidMoveToWindow
1617 [self resetTrackingRect];
1619 if ([self window]) {
1620 // While in the view hierarchy, observe WebPreferencesChangedNotification so that we can start/stop depending
1621 // on whether plugins are enabled.
1622 [[NSNotificationCenter defaultCenter] addObserver:self
1623 selector:@selector(preferencesHaveChanged:)
1624 name:WebPreferencesChangedNotification
1627 // View moved to an actual window. Start it if not already started.
1629 [self restartNullEvents];
1630 [self addWindowObservers];
1631 } else if ([[self webView] hostWindow]) {
1632 // View moved out of an actual window, but still has a host window.
1633 // Call setWindow to explicitly "clip out" the plug-in from sight.
1634 // FIXME: It would be nice to do this where we call stopNullEvents in viewWillMoveToWindow.
1635 [self updateAndSetWindow];
1639 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
1641 if (!hostWindow && ![self window]) {
1642 // View will have no associated windows.
1645 // Remove WebPreferencesChangedNotification observer -- we will observe once again when we move back into the window
1646 [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1650 - (void)viewDidMoveToHostWindow
1652 if ([[self webView] hostWindow]) {
1653 // View now has an associated window. Start it if not already started.
1658 #pragma mark NOTIFICATIONS
1660 - (void)windowWillClose:(NSNotification *)notification
1665 - (void)windowBecameKey:(NSNotification *)notification
1667 [self sendActivateEvent:YES];
1668 [self setNeedsDisplay:YES];
1669 [self restartNullEvents];
1670 SetUserFocusWindow((WindowRef)[[self window] windowRef]);
1673 - (void)windowResignedKey:(NSNotification *)notification
1675 [self sendActivateEvent:NO];
1676 [self setNeedsDisplay:YES];
1677 [self restartNullEvents];
1680 - (void)windowDidMiniaturize:(NSNotification *)notification
1682 [self stopNullEvents];
1685 - (void)windowDidDeminiaturize:(NSNotification *)notification
1687 [self restartNullEvents];
1690 - (void)loginWindowDidSwitchFromUser:(NSNotification *)notification
1692 [self stopNullEvents];
1695 -(void)loginWindowDidSwitchToUser:(NSNotification *)notification
1697 [self restartNullEvents];
1700 - (void)preferencesHaveChanged:(NSNotification *)notification
1702 WebPreferences *preferences = [[self webView] preferences];
1703 BOOL arePlugInsEnabled = [preferences arePlugInsEnabled];
1705 if ([notification object] == preferences && isStarted != arePlugInsEnabled) {
1706 if (arePlugInsEnabled) {
1707 if ([self currentWindow]) {
1712 [self setNeedsDisplay:YES];
1717 - (NPObject *)createPluginScriptableObject
1722 NPObject *value = NULL;
1723 [self willCallPlugInFunction];
1724 NPError error = NPP_GetValue(instance, NPPVpluginScriptableNPObject, &value);
1725 [self didCallPlugInFunction];
1726 if (error != NPERR_NO_ERROR)
1732 - (void)willCallPlugInFunction
1734 // Could try to prevent infinite recursion here, but it's probably not worth the effort.
1735 pluginFunctionCallDepth++;
1738 - (void)didCallPlugInFunction
1740 ASSERT(pluginFunctionCallDepth > 0);
1741 pluginFunctionCallDepth--;
1743 // If -stop was called while we were calling into a plug-in function, and we're no longer
1744 // inside a plug-in function, stop now.
1745 if (pluginFunctionCallDepth == 0 && shouldStopSoon) {
1746 shouldStopSoon = NO;
1753 @implementation WebBaseNetscapePluginView (WebNPPCallbacks)
1755 - (NSMutableURLRequest *)requestWithURLCString:(const char *)URLCString
1760 CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, URLCString, kCFStringEncodingISOLatin1);
1761 ASSERT(string); // All strings should be representable in ISO Latin 1
1763 NSString *URLString = [(NSString *)string _web_stringByStrippingReturnCharacters];
1764 NSURL *URL = [NSURL _web_URLWithDataAsString:URLString relativeToURL:baseURL];
1769 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
1770 [request _web_setHTTPReferrer:core([self webFrame])->referrer()];
1774 - (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest
1776 // FIXME: Is this isStarted check needed here? evaluateJavaScriptPluginRequest should not be called
1777 // if we are stopped since this method is called after a delay and we call
1778 // cancelPreviousPerformRequestsWithTarget inside of stop.
1783 NSURL *URL = [[JSPluginRequest request] URL];
1784 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1787 NSString *result = [[[self webFrame] _bridge] stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]];
1789 // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop.
1794 if ([JSPluginRequest frameName] != nil) {
1795 // FIXME: If the result is a string, we probably want to put that string into the frame.
1796 if ([JSPluginRequest sendNotification]) {
1797 [self willCallPlugInFunction];
1798 NPP_URLNotify(instance, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]);
1799 [self didCallPlugInFunction];
1801 } else if ([result length] > 0) {
1802 // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does.
1803 NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding];
1804 WebBaseNetscapePluginStream *stream = [[WebBaseNetscapePluginStream alloc] initWithRequestURL:URL
1805 pluginPointer:instance
1806 notifyData:[JSPluginRequest notifyData]
1807 sendNotification:[JSPluginRequest sendNotification]];
1808 [stream startStreamResponseURL:URL
1809 expectedContentLength:[JSData length]
1810 lastModifiedDate:nil
1811 MIMEType:@"text/plain"];
1812 [stream receivedData:JSData];
1813 [stream finishedLoadingWithData:JSData];
1818 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
1822 WebPluginRequest *pluginRequest = [pendingFrameLoads objectForKey:webFrame];
1823 ASSERT(pluginRequest != nil);
1824 ASSERT([pluginRequest sendNotification]);
1826 [self willCallPlugInFunction];
1827 NPP_URLNotify(instance, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]);
1828 [self didCallPlugInFunction];
1830 [pendingFrameLoads removeObjectForKey:webFrame];
1831 [webFrame _setInternalLoadDelegate:nil];
1834 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
1836 NPReason reason = NPRES_DONE;
1838 reason = [WebBaseNetscapePluginStream reasonForError:error];
1840 [self webFrame:webFrame didFinishLoadWithReason:reason];
1843 - (void)loadPluginRequest:(WebPluginRequest *)pluginRequest
1845 NSURLRequest *request = [pluginRequest request];
1846 NSString *frameName = [pluginRequest frameName];
1847 WebFrame *frame = nil;
1849 NSURL *URL = [request URL];
1850 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1852 ASSERT(frameName || JSString);
1855 // FIXME - need to get rid of this window creation which
1856 // bypasses normal targeted link handling
1857 frame = [[self webFrame] findFrameNamed:frameName];
1860 WebView *newWebView = nil;
1861 WebView *currentWebView = [self webView];
1862 id wd = [currentWebView UIDelegate];
1863 if ([wd respondsToSelector:@selector(webView:createWebViewWithRequest:)]) {
1864 newWebView = [wd webView:currentWebView createWebViewWithRequest:nil];
1866 newWebView = [[WebDefaultUIDelegate sharedUIDelegate] webView:currentWebView createWebViewWithRequest:nil];
1868 frame = [newWebView mainFrame];
1869 core(frame)->tree()->setName(frameName);
1870 [[newWebView _UIDelegateForwarder] webViewShow:newWebView];
1875 ASSERT(frame == nil || [self webFrame] == frame);
1876 [self evaluateJavaScriptPluginRequest:pluginRequest];
1878 [frame loadRequest:request];
1879 if ([pluginRequest sendNotification]) {
1880 // Check if another plug-in view or even this view is waiting for the frame to load.
1881 // If it is, tell it that the load was cancelled because it will be anyway.
1882 WebBaseNetscapePluginView *view = [frame _internalLoadDelegate];
1884 ASSERT([view isKindOfClass:[WebBaseNetscapePluginView class]]);
1885 [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK];
1887 [pendingFrameLoads _webkit_setObject:pluginRequest forUncopiedKey:frame];
1888 [frame _setInternalLoadDelegate:self];
1893 - (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification
1895 NSURL *URL = [request URL];
1898 return NPERR_INVALID_URL;
1900 // don't let a plugin start any loads if it is no longer part of a document that is being
1902 if ([[self dataSource] _documentLoader] != [[self webFrame] _frameLoader]->activeDocumentLoader())
1903 return NPERR_GENERIC_ERROR;
1905 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1906 if (JSString != nil) {
1907 if (![[[self webView] preferences] isJavaScriptEnabled]) {
1908 // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does.
1909 return NPERR_GENERIC_ERROR;
1910 } else if (cTarget == NULL && mode == NP_FULL) {
1911 // Don't allow a JavaScript request from a standalone plug-in that is self-targetted
1912 // because this can cause the user to be redirected to a blank page (3424039).
1913 return NPERR_INVALID_PARAM;
1917 if (cTarget || JSString) {
1918 // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't
1919 // want to potentially kill the plug-in inside of its URL request.
1920 NSString *target = nil;
1922 // Find the frame given the target string.
1923 target = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, cTarget, kCFStringEncodingWindowsLatin1);
1926 WebFrame *frame = [self webFrame];
1927 if (JSString != nil && target != nil && [frame findFrameNamed:target] != frame) {
1928 // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
1930 return NPERR_INVALID_PARAM;
1933 WebPluginRequest *pluginRequest = [[WebPluginRequest alloc] initWithRequest:request frameName:target notifyData:notifyData sendNotification:sendNotification didStartFromUserGesture:currentEventIsUserGesture];
1934 [self performSelector:@selector(loadPluginRequest:) withObject:pluginRequest afterDelay:0];
1935 [pluginRequest release];
1939 WebNetscapePluginStream *stream = [[WebNetscapePluginStream alloc] initWithRequest:request
1940 pluginPointer:instance
1941 notifyData:notifyData
1942 sendNotification:sendNotification];
1944 return NPERR_INVALID_URL;
1946 [streams addObject:stream];
1951 return NPERR_NO_ERROR;
1954 -(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData
1956 LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget);
1958 NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1959 return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES];
1962 -(NPError)getURL:(const char *)URLCString target:(const char *)cTarget
1964 LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget);
1966 NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1967 return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO];
1970 - (NPError)_postURL:(const char *)URLCString
1971 target:(const char *)target
1973 buf:(const char *)buf
1975 notifyData:(void *)notifyData
1976 sendNotification:(BOOL)sendNotification
1977 allowHeaders:(BOOL)allowHeaders
1979 if (!URLCString || !len || !buf) {
1980 return NPERR_INVALID_PARAM;
1983 NSData *postData = nil;
1986 // If we're posting a file, buf is either a file URL or a path to the file.
1987 NSString *bufString = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingWindowsLatin1);
1989 return NPERR_INVALID_PARAM;
1991 NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString];
1993 if ([fileURL isFileURL]) {
1994 path = [fileURL path];
1998 postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]];
1999 CFRelease(bufString);
2001 return NPERR_FILE_NOT_FOUND;
2004 postData = [NSData dataWithBytes:buf length:len];
2007 if ([postData length] == 0) {
2008 return NPERR_INVALID_PARAM;
2011 NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
2012 [request setHTTPMethod:@"POST"];
2015 if ([postData _web_startsWithBlankLine]) {
2016 postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)];
2018 WebNSInteger location = [postData _web_locationAfterFirstBlankLine];
2019 if (location != NSNotFound) {
2020 // If the blank line is somewhere in the middle of postData, everything before is the header.
2021 NSData *headerData = [postData subdataWithRange:NSMakeRange(0, location)];
2022 NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields];
2023 unsigned dataLength = [postData length] - location;
2025 // Sometimes plugins like to set Content-Length themselves when they post,
2026 // but WebFoundation does not like that. So we will remove the header
2027 // and instead truncate the data to the requested length.
2028 NSString *contentLength = [header objectForKey:@"Content-Length"];
2030 if (contentLength != nil)
2031 dataLength = MIN((unsigned)[contentLength intValue], dataLength);
2032 [header removeObjectForKey:@"Content-Length"];
2034 if ([header count] > 0) {
2035 [request setAllHTTPHeaderFields:header];
2037 // Everything after the blank line is the actual content of the POST.
2038 postData = [postData subdataWithRange:NSMakeRange(location, dataLength)];
2042 if ([postData length] == 0) {
2043 return NPERR_INVALID_PARAM;
2047 // Plug-ins expect to receive uncached data when doing a POST (3347134).
2048 [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
2049 [request setHTTPBody:postData];
2051 return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification];
2054 - (NPError)postURLNotify:(const char *)URLCString
2055 target:(const char *)target
2057 buf:(const char *)buf
2059 notifyData:(void *)notifyData
2061 LOG(Plugins, "NPN_PostURLNotify: %s", URLCString);
2062 return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:notifyData sendNotification:YES allowHeaders:YES];
2065 -(NPError)postURL:(const char *)URLCString
2066 target:(const char *)target
2068 buf:(const char *)buf
2071 LOG(Plugins, "NPN_PostURL: %s", URLCString);
2072 // As documented, only allow headers to be specified via NPP_PostURL when using a file.
2073 return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:NULL sendNotification:NO allowHeaders:file];
2076 -(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream
2078 LOG(Plugins, "NPN_NewStream");
2079 return NPERR_GENERIC_ERROR;
2082 -(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer
2084 LOG(Plugins, "NPN_Write");
2085 return NPERR_GENERIC_ERROR;
2088 -(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason
2090 LOG(Plugins, "NPN_DestroyStream");
2091 if (!stream->ndata) {
2092 return NPERR_INVALID_INSTANCE_ERROR;
2094 WebBaseNetscapePluginStream *browserStream = (WebBaseNetscapePluginStream *)stream->ndata;
2095 [browserStream cancelLoadAndDestroyStreamWithError:[browserStream errorForReason:reason]];
2096 return NPERR_NO_ERROR;
2099 - (const char *)userAgent
2101 return [[[self webView] userAgentForURL:baseURL] lossyCString];
2104 -(void)status:(const char *)message
2107 LOG_ERROR("NPN_Status passed a NULL status message");
2111 CFStringRef status = CFStringCreateWithCString(NULL, message, kCFStringEncodingWindowsLatin1);
2112 LOG(Plugins, "NPN_Status: %@", status);
2113 WebView *wv = [self webView];
2114 [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status];
2118 -(void)invalidateRect:(NPRect *)invalidRect
2120 LOG(Plugins, "NPN_InvalidateRect");
2121 [self setNeedsDisplayInRect:NSMakeRect(invalidRect->left, invalidRect->top,
2122 (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)];
2125 - (void)invalidateRegion:(NPRegion)invalidRegion
2127 LOG(Plugins, "NPN_InvalidateRegion");
2128 NSRect invalidRect = NSZeroRect;
2129 switch (drawingModel) {
2130 #ifndef NP_NO_QUICKDRAW
2131 case NPDrawingModelQuickDraw:
2134 GetRegionBounds((NPQDRegion)invalidRegion, &qdRect);
2135 invalidRect = NSMakeRect(qdRect.left, qdRect.top, qdRect.right - qdRect.left, qdRect.bottom - qdRect.top);
2138 #endif /* NP_NO_QUICKDRAW */
2140 case NPDrawingModelCoreGraphics:
2141 case NPDrawingModelOpenGL:
2143 CGRect cgRect = CGPathGetBoundingBox((NPCGRegion)invalidRegion);
2144 invalidRect = *(NSRect *)&cgRect;
2149 ASSERT_NOT_REACHED();
2153 [self setNeedsDisplayInRect:invalidRect];
2158 LOG(Plugins, "forceRedraw");
2159 [self setNeedsDisplay:YES];
2160 [[self window] displayIfNeeded];
2163 - (NPError)getVariable:(NPNVariable)variable value:(void *)value
2166 case NPNVWindowNPObject:
2168 NPObject *windowScriptObject = core([self webFrame])->windowScriptNPObject();
2170 // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
2171 if (windowScriptObject)
2172 _NPN_RetainObject(windowScriptObject);
2174 void **v = (void **)value;
2175 *v = windowScriptObject;
2177 return NPERR_NO_ERROR;
2180 case NPNVPluginElementNPObject:
2183 return NPERR_GENERIC_ERROR;
2185 NPObject *plugInScriptObject = (NPObject *)[element _NPObject];
2187 // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
2188 if (plugInScriptObject)
2189 _NPN_RetainObject(plugInScriptObject);
2191 void **v = (void **)value;
2192 *v = plugInScriptObject;
2194 return NPERR_NO_ERROR;
2197 case NPNVpluginDrawingModel:
2199 *(NPDrawingModel *)value = drawingModel;
2200 return NPERR_NO_ERROR;
2203 #ifndef NP_NO_QUICKDRAW
2204 case NPNVsupportsQuickDrawBool:
2206 *(NPBool *)value = TRUE;
2207 return NPERR_NO_ERROR;
2209 #endif /* NP_NO_QUICKDRAW */
2211 case NPNVsupportsCoreGraphicsBool:
2213 *(NPBool *)value = TRUE;
2214 return NPERR_NO_ERROR;
2217 case NPNVsupportsOpenGLBool:
2219 *(NPBool *)value = TRUE;
2220 return NPERR_NO_ERROR;
2227 return NPERR_GENERIC_ERROR;
2230 - (NPError)setVariable:(NPPVariable)variable value:(void *)value
2233 case NPPVpluginWindowBool:
2235 NPWindowType newWindowType = (value ? NPWindowTypeWindow : NPWindowTypeDrawable);
2237 // Redisplay if window type is changing (some drawing models can only have their windows set while updating).
2238 if (newWindowType != window.type)
2239 [self setNeedsDisplay:YES];
2241 window.type = newWindowType;
2244 case NPPVpluginTransparentBool:
2246 BOOL newTransparent = (value != 0);
2248 // Redisplay if transparency is changing
2249 if (isTransparent != newTransparent)
2250 [self setNeedsDisplay:YES];
2252 isTransparent = newTransparent;
2254 return NPERR_NO_ERROR;
2257 case NPNVpluginDrawingModel:
2259 // Can only set drawing model inside NPP_New()
2260 if (self != [[self class] currentPluginView])
2261 return NPERR_GENERIC_ERROR;
2263 // Check for valid, supported drawing model
2264 NPDrawingModel newDrawingModel = (NPDrawingModel)(uintptr_t)value;
2265 switch (newDrawingModel) {
2266 // Supported drawing models:
2267 #ifndef NP_NO_QUICKDRAW
2268 case NPDrawingModelQuickDraw:
2270 case NPDrawingModelCoreGraphics:
2271 case NPDrawingModelOpenGL:
2272 drawingModel = newDrawingModel;
2273 return NPERR_NO_ERROR;
2275 // Unsupported (or unknown) drawing models:
2277 LOG(Plugins, "Plugin %@ uses unsupported drawing model: %d", plugin, drawingModel);
2278 return NPERR_GENERIC_ERROR;
2283 return NPERR_GENERIC_ERROR;
2289 @implementation WebPluginRequest
2291 - (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture
2294 _didStartFromUserGesture = currentEventIsUserGesture;
2295 _request = [request retain];
2296 _frameName = [frameName retain];
2297 _notifyData = notifyData;
2298 _sendNotification = sendNotification;
2305 [_frameName release];
2309 - (NSURLRequest *)request
2314 - (NSString *)frameName
2319 - (BOOL)isCurrentEventUserGesture
2321 return _didStartFromUserGesture;
2324 - (BOOL)sendNotification
2326 return _sendNotification;
2329 - (void *)notifyData
2336 @implementation WebBaseNetscapePluginView (Internal)
2338 - (void)_viewHasMoved
2340 // All of the work this method does may safely be skipped if the view is not in a window. When the view
2341 // is moved back into a window, everything should be set up correctly.
2345 if (drawingModel == NPDrawingModelOpenGL)
2346 [self _reshapeAGLWindow];
2348 #ifndef NP_NO_QUICKDRAW
2349 if (drawingModel == NPDrawingModelQuickDraw)
2350 [self tellQuickTimeToChill];
2352 [self updateAndSetWindow];
2353 [self resetTrackingRect];
2355 // Check to see if the plugin view is completely obscured (scrolled out of view, for example).
2356 // For performance reasons, we send null events at a lower rate to plugins which are obscured.
2357 BOOL oldIsObscured = isCompletelyObscured;
2358 isCompletelyObscured = NSIsEmptyRect([self visibleRect]);
2359 if (isCompletelyObscured != oldIsObscured)
2360 [self restartNullEvents];
2363 - (NSBitmapImageRep *)_printedPluginBitmap
2365 #ifdef NP_NO_QUICKDRAW
2368 // Cannot print plugins that do not implement NPP_Print
2372 // This NSBitmapImageRep will share its bitmap buffer with a GWorld that the plugin will draw into.
2373 // The bitmap is created in 32-bits-per-pixel ARGB format, which is the default GWorld pixel format.
2374 NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
2375 pixelsWide:window.width
2376 pixelsHigh:window.height
2381 colorSpaceName:NSDeviceRGBColorSpace
2382 bitmapFormat:NSAlphaFirstBitmapFormat
2384 bitsPerPixel:0] autorelease];
2387 // Create a GWorld with the same underlying buffer into which the plugin can draw
2388 Rect printGWorldBounds;
2389 SetRect(&printGWorldBounds, 0, 0, window.width, window.height);
2390 GWorldPtr printGWorld;
2391 if (NewGWorldFromPtr(&printGWorld,
2397 (Ptr)[bitmap bitmapData],
2398 [bitmap bytesPerRow]) != noErr) {
2399 LOG_ERROR("Could not create GWorld for printing");
2403 /// Create NPWindow for the GWorld
2404 NPWindow printNPWindow;
2405 printNPWindow.window = &printGWorld; // Normally this is an NP_Port, but when printing it is the actual CGrafPtr
2406 printNPWindow.x = 0;
2407 printNPWindow.y = 0;
2408 printNPWindow.width = window.width;
2409 printNPWindow.height = window.height;
2410 printNPWindow.clipRect.top = 0;
2411 printNPWindow.clipRect.left = 0;
2412 printNPWindow.clipRect.right = window.width;
2413 printNPWindow.clipRect.bottom = window.height;
2414 printNPWindow.type = NPWindowTypeDrawable; // Offscreen graphics port as opposed to a proper window
2416 // Create embed-mode NPPrint
2418 npPrint.mode = NP_EMBED;
2419 npPrint.print.embedPrint.window = printNPWindow;
2420 npPrint.print.embedPrint.platformPrint = printGWorld;
2422 // Tell the plugin to print into the GWorld
2423 [self willCallPlugInFunction];
2424 NPP_Print(instance, &npPrint);
2425 [self didCallPlugInFunction];
2427 // Don't need the GWorld anymore
2428 DisposeGWorld(printGWorld);
2434 - (BOOL)_createAGLContextIfNeeded
2436 ASSERT(drawingModel == NPDrawingModelOpenGL);
2438 // Do nothing (but indicate success) if the AGL context already exists
2442 switch (window.type) {
2443 case NPWindowTypeWindow:
2444 return [self _createWindowedAGLContext];
2446 case NPWindowTypeDrawable:
2447 return [self _createWindowlessAGLContext];
2450 ASSERT_NOT_REACHED();
2455 - (BOOL)_createWindowedAGLContext
2457 ASSERT(drawingModel == NPDrawingModelOpenGL);
2458 ASSERT(!aglContext);
2460 ASSERT([self window]);
2462 GLint pixelFormatAttributes[] = {
2474 // Choose AGL pixel format
2475 AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL, 0, pixelFormatAttributes);
2477 LOG_ERROR("Could not find suitable AGL pixel format: %s", aglErrorString(aglGetError()));
2481 // Create AGL context
2482 aglContext = aglCreateContext(pixelFormat, NULL);
2483 aglDestroyPixelFormat(pixelFormat);
2485 LOG_ERROR("Could not create AGL context: %s", aglErrorString(aglGetError()));
2489 // Create AGL window
2490 aglWindow = [[NSWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
2492 LOG_ERROR("Could not create window for AGL drawable.");
2496 // AGL window should allow clicks to go through -- mouse events are tracked by WebCore
2497 [aglWindow setIgnoresMouseEvents:YES];
2499 // Make sure the window is not opaque -- windowed plug-ins cannot layer with other page elements
2500 [aglWindow setOpaque:YES];
2502 // Position and order in the AGL window
2503 [self _reshapeAGLWindow];
2505 // Attach the AGL context to its window
2507 #ifdef AGL_VERSION_3_0
2508 success = aglSetWindowRef(aglContext, (WindowRef)[aglWindow windowRef]);
2510 success = aglSetDrawable(aglContext, (AGLDrawable)GetWindowPort((WindowRef)[aglWindow windowRef]));
2513 LOG_ERROR("Could not set AGL drawable: %s", aglErrorString(aglGetError()));
2514 aglDestroyContext(aglContext);
2522 - (BOOL)_createWindowlessAGLContext
2524 ASSERT(drawingModel == NPDrawingModelOpenGL);
2525 ASSERT(!aglContext);
2528 GLint pixelFormatAttributes[] = {
2539 // Choose AGL pixel format
2540 AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL, 0, pixelFormatAttributes);
2542 LOG_ERROR("Could not find suitable AGL pixel format: %s", aglErrorString(aglGetError()));
2546 // Create AGL context
2547 aglContext = aglCreateContext(pixelFormat, NULL);
2548 aglDestroyPixelFormat(pixelFormat);
2550 LOG_ERROR("Could not create AGL context: %s", aglErrorString(aglGetError()));
2554 // Create offscreen buffer for AGL context
2555 NSSize boundsSize = [self bounds].size;
2556 GLvoid *offscreenBuffer = (GLvoid *)malloc(static_cast<size_t>(boundsSize.width * boundsSize.height * 4));
2557 if (!offscreenBuffer) {
2558 LOG_ERROR("Could not allocate offscreen buffer for AGL context");
2559 aglDestroyContext(aglContext);
2564 // Attach AGL context to offscreen buffer
2565 CGLContextObj cglContext = [self _cglContext];
2566 CGLError error = CGLSetOffScreen(cglContext, static_cast<long>(boundsSize.width), static_cast<long>(boundsSize.height), static_cast<long>(boundsSize.width * 4), offscreenBuffer);
2568 LOG_ERROR("Could not set offscreen buffer for AGL context: %d", error);
2569 aglDestroyContext(aglContext);
2577 - (CGLContextObj)_cglContext
2579 ASSERT(drawingModel == NPDrawingModelOpenGL);
2581 CGLContextObj cglContext = NULL;
2582 if (!aglGetCGLContext(aglContext, (void **)&cglContext) || !cglContext)
2583 LOG_ERROR("Could not get CGL context for AGL context: %s", aglErrorString(aglGetError()));
2588 - (BOOL)_getAGLOffscreenBuffer:(GLvoid **)outBuffer width:(GLsizei *)outWidth height:(GLsizei *)outHeight
2590 ASSERT(drawingModel == NPDrawingModelOpenGL);
2599 // Only windowless plug-ins have offscreen buffers
2600 if (window.type != NPWindowTypeDrawable)
2603 CGLContextObj cglContext = [self _cglContext];
2607 GLsizei width, height;
2609 void *offscreenBuffer = NULL;
2610 CGLError error = CGLGetOffScreen(cglContext, &width, &height, &rowBytes, &offscreenBuffer);
2611 if (error || !offscreenBuffer) {
2612 LOG_ERROR("Could not get offscreen buffer for AGL context: %d", error);
2617 *outBuffer = offscreenBuffer;
2621 *outHeight = height;
2626 - (void)_destroyAGLContext
2628 ASSERT(drawingModel == NPDrawingModelOpenGL);
2634 // If this is a windowless plug-in, free its offscreen buffer
2635 GLvoid *offscreenBuffer;
2636 if ([self _getAGLOffscreenBuffer:&offscreenBuffer width:NULL height:NULL])
2637 free(offscreenBuffer);
2639 // Detach context from the AGL window
2640 #ifdef AGL_VERSION_3_0
2641 aglSetWindowRef(aglContext, NULL);
2643 aglSetDrawable(aglContext, NULL);
2646 // Destroy the context
2647 aglDestroyContext(aglContext);
2651 // Destroy the AGL window
2653 [self _hideAGLWindow];
2658 - (void)_reshapeAGLWindow
2660 ASSERT(drawingModel == NPDrawingModelOpenGL);
2665 switch (window.type) {
2666 case NPWindowTypeWindow:
2671 // The AGL window is being reshaped because the plugin view has moved. Since the view has moved, it will soon redraw.
2672 // We want the AGL window to update at the same time as its underlying view. So, we disable screen updates until the
2673 // plugin view's window flushes.
2674 NSWindow *browserWindow = [self window];
2675 ASSERT(browserWindow);
2676 [browserWindow disableScreenUpdatesUntilFlush];
2678 // Add the AGL window as a child of the main window if necessary
2679 if ([aglWindow parentWindow] != browserWindow)
2680 [browserWindow addChildWindow:aglWindow ordered:NSWindowAbove];
2682 // Update the AGL window frame
2683 NSRect aglWindowFrame = [self convertRect:[self visibleRect] toView:nil];
2684 aglWindowFrame.origin = [browserWindow convertBaseToScreen:aglWindowFrame.origin];
2685 [aglWindow setFrame:aglWindowFrame display:NO];
2687 // Update the AGL context
2688 aglUpdateContext(aglContext);
2692 case NPWindowTypeDrawable:
2694 // Get offscreen buffer; we can skip this step if we don't have one yet
2695 GLvoid *offscreenBuffer;
2696 GLsizei width, height;
2697 if (![self _getAGLOffscreenBuffer:&offscreenBuffer width:&width height:&height] || !offscreenBuffer)
2700 // Don't resize the offscreen buffer if it's already the same size as the view bounds
2701 NSSize boundsSize = [self bounds].size;
2702 if (boundsSize.width == width && boundsSize.height == height)
2705 // Resize the offscreen buffer
2706 offscreenBuffer = realloc(offscreenBuffer, static_cast<size_t>(boundsSize.width * boundsSize.height * 4));
2707 if (!offscreenBuffer) {
2708 LOG_ERROR("Could not allocate offscreen buffer for AGL context");
2712 // Update the offscreen
2713 CGLContextObj cglContext = [self _cglContext];
2714 CGLError error = CGLSetOffScreen(cglContext, static_cast<long>(boundsSize.width), static_cast<long>(boundsSize.height), static_cast<long>(boundsSize.width * 4), offscreenBuffer);
2716 LOG_ERROR("Could not set offscreen buffer for AGL context: %d", error);
2720 // Update the AGL context
2721 aglUpdateContext(aglContext);
2726 ASSERT_NOT_REACHED();
2731 - (void)_hideAGLWindow
2733 ASSERT(drawingModel == NPDrawingModelOpenGL);
2738 // aglWindow should only be set for a windowed OpenGL plug-in
2739 ASSERT(window.type == NPWindowTypeWindow);
2741 NSWindow *parentWindow = [aglWindow parentWindow];
2743 // Disable screen updates so that this AGL window orders out atomically with other plugins' AGL windows
2744 [parentWindow disableScreenUpdatesUntilFlush];
2745 ASSERT(parentWindow == [self window]);
2746 [parentWindow removeChildWindow:aglWindow];
2748 [aglWindow orderOut:nil];
2751 - (NSImage *)_aglOffscreenImageForDrawingInRect:(NSRect)drawingInRect
2753 ASSERT(drawingModel == NPDrawingModelOpenGL);
2755 CGLContextObj cglContext = [self _cglContext];
2759 // Get the offscreen buffer
2760 GLvoid *offscreenBuffer;
2761 GLsizei width, height;
2762 if (![self _getAGLOffscreenBuffer:&offscreenBuffer width:&width height:&height])
2765 unsigned char *plane = (unsigned char *)offscreenBuffer;
2767 #if defined(__i386__) || defined(__x86_64__)
2768 // Make rect inside the offscreen buffer because we're about to directly modify the bits inside drawingInRect
2769 NSRect rect = NSIntegralRect(NSIntersectionRect(drawingInRect, NSMakeRect(0, 0, width, height)));
2771 // The offscreen buffer, being an OpenGL framebuffer, is in BGRA format on x86. We need to swap the blue and red channels before
2772 // wrapping the buffer in an NSBitmapImageRep, which only supports RGBA and ARGB.
2773 // On PowerPC, the OpenGL framebuffer is in ARGB format. Since that is a format that NSBitmapImageRep supports, all that is
2774 // needed on PowerPC is to pass the NSAlphaFirstBitmapFormat flag when creating the NSBitmapImageRep. On x86, we need to swap the
2775 // framebuffer color components such that they are in ARGB order, as they are on PowerPC.
2776 // 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.
2777 // Since we know what region of the image will ultimately be drawn to screen (drawingInRect), we restrict the channel swapping to
2778 // just that region within the offscreen buffer.
2779 if (!WebConvertBGRAToARGB(plane, width * 4, (int)rect.origin.x, (int)rect.origin.y, (int)rect.size.width, (int)rect.size.height))
2781 #endif /* defined(__i386__) || defined(__x86_64__) */
2783 NSBitmapImageRep *aglBitmap = [[NSBitmapImageRep alloc]
2784 initWithBitmapDataPlanes:&plane
2791 colorSpaceName:NSDeviceRGBColorSpace
2792 bitmapFormat:NSAlphaFirstBitmapFormat
2793 bytesPerRow:width * 4
2796 LOG_ERROR("Could not create bitmap for AGL offscreen buffer");
2800 // Wrap the bitmap in an NSImage. This allocation isn't very expensive -- the actual image data is already in the bitmap rep
2801 NSImage *aglImage = [[[NSImage alloc] initWithSize:[aglBitmap size]] autorelease];
2802 [aglImage addRepresentation:aglBitmap];
2803 [aglBitmap release];
2810 @implementation NSData (PluginExtras)
2812 - (BOOL)_web_startsWithBlankLine
2814 return [self length] > 0 && ((const char *)[self bytes])[0] == '\n';
2818 - (WebNSInteger)_web_locationAfterFirstBlankLine
2820 const char *bytes = (const char *)[self bytes];
2821 unsigned length = [self length];
2824 for (i = 0; i < length - 4; i++) {
2826 // Support for Acrobat. It sends "\n\n".
2827 if (bytes[i] == '\n' && bytes[i+1] == '\n') {
2831 // Returns the position after 2 CRLF's or 1 CRLF if it is the first line.
2832 if (bytes[i] == '\r' && bytes[i+1] == '\n') {
2836 } else if (bytes[i] == '\n') {
2837 // Support for Director. It sends "\r\n\n" (3880387).
2839 } else if (bytes[i] == '\r' && bytes[i+1] == '\n') {
2840 // Support for Flash. It sends "\r\n\r\n" (3758113).