2 * Copyright (C) 2005, 2006, 2007 Apple 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 #if ENABLE(NETSCAPE_PLUGIN_API)
31 #import "WebBaseNetscapePluginView.h"
33 #import "WebDataSourceInternal.h"
34 #import "WebDefaultUIDelegate.h"
35 #import "WebFrameInternal.h"
36 #import "WebFrameView.h"
37 #import "WebGraphicsExtras.h"
38 #import "WebKitLogging.h"
39 #import "WebKitNSStringExtras.h"
40 #import "WebKitSystemInterface.h"
41 #import "WebNSDataExtras.h"
42 #import "WebNSDictionaryExtras.h"
43 #import "WebNSObjectExtras.h"
44 #import "WebNSURLExtras.h"
45 #import "WebNSURLRequestExtras.h"
46 #import "WebNSViewExtras.h"
47 #import "WebNetscapePluginPackage.h"
48 #import "WebNetscapePluginStream.h"
49 #import "WebNetscapePluginEventHandler.h"
50 #import "WebNullPluginView.h"
51 #import "WebPreferences.h"
52 #import "WebViewInternal.h"
53 #import "WebUIDelegatePrivate.h"
54 #import <Carbon/Carbon.h>
55 #import <JavaScriptCore/Assertions.h>
56 #import <JavaScriptCore/JSLock.h>
57 #import <WebCore/npruntime_impl.h>
58 #import <WebCore/Document.h>
59 #import <WebCore/Element.h>
60 #import <WebCore/Frame.h>
61 #import <WebCore/FrameLoader.h>
62 #import <WebCore/FrameTree.h>
63 #import <WebCore/Page.h>
64 #import <WebCore/SoftLinking.h>
65 #import <WebCore/WebCoreObjCExtras.h>
66 #import <WebKit/nptextinput.h>
67 #import <WebKit/DOMPrivate.h>
68 #import <WebKit/WebUIDelegate.h>
69 #import <objc/objc-runtime.h>
71 using namespace WebCore;
73 #define LoginWindowDidSwitchFromUserNotification @"WebLoginWindowDidSwitchFromUserNotification"
74 #define LoginWindowDidSwitchToUserNotification @"WebLoginWindowDidSwitchToUserNotification"
76 SOFT_LINK_FRAMEWORK(OpenGL)
77 SOFT_LINK_FRAMEWORK(AGL)
79 SOFT_LINK(OpenGL, CGLGetOffScreen, CGLError, (CGLContextObj ctx, GLsizei *width, GLsizei *height, GLint *rowbytes, void **baseaddr), (ctx, width, height, rowbytes, baseaddr))
80 SOFT_LINK(OpenGL, CGLSetOffScreen, CGLError, (CGLContextObj ctx, GLsizei width, GLsizei height, GLint rowbytes, void *baseaddr), (ctx, width, height, rowbytes, baseaddr))
81 SOFT_LINK(OpenGL, glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height), (x, y, width, height))
82 SOFT_LINK(AGL, aglCreateContext, AGLContext, (AGLPixelFormat pix, AGLContext share), (pix, share))
83 SOFT_LINK(AGL, aglSetWindowRef, GLboolean, (AGLContext ctx, WindowRef window), (ctx, window))
84 SOFT_LINK(AGL, aglSetDrawable, GLboolean, (AGLContext ctx, AGLDrawable draw), (ctx, draw))
85 #ifndef BUILDING_ON_TIGER
86 SOFT_LINK(AGL, aglChoosePixelFormat, AGLPixelFormat, (const void *gdevs, GLint ndev, const GLint *attribs), (gdevs, ndev, attribs))
88 SOFT_LINK(AGL, aglChoosePixelFormat, AGLPixelFormat, (const AGLDevice *gdevs, GLint ndev, const GLint *attribs), (gdevs, ndev, attribs))
90 SOFT_LINK(AGL, aglDestroyPixelFormat, void, (AGLPixelFormat pix), (pix))
91 SOFT_LINK(AGL, aglDestroyContext, GLboolean, (AGLContext ctx), (ctx))
92 SOFT_LINK(AGL, aglGetCGLContext, GLboolean, (AGLContext ctx, void **cgl_ctx), (ctx, cgl_ctx))
93 SOFT_LINK(AGL, aglGetCurrentContext, AGLContext, (void), ())
94 SOFT_LINK(AGL, aglSetCurrentContext, GLboolean, (AGLContext ctx), (ctx))
95 SOFT_LINK(AGL, aglGetError, GLenum, (void), ())
96 SOFT_LINK(AGL, aglUpdateContext, GLboolean, (AGLContext ctx), (ctx))
97 SOFT_LINK(AGL, aglErrorString, const GLubyte *, (GLenum code), (code))
99 @interface WebBaseNetscapePluginView (Internal)
100 - (void)_viewHasMoved;
101 - (NPError)_createPlugin;
102 - (void)_destroyPlugin;
103 - (NSBitmapImageRep *)_printedPluginBitmap;
104 - (BOOL)_createAGLContextIfNeeded;
105 - (BOOL)_createWindowedAGLContext;
106 - (BOOL)_createWindowlessAGLContext;
107 - (CGLContextObj)_cglContext;
108 - (BOOL)_getAGLOffscreenBuffer:(GLvoid **)outBuffer width:(GLsizei *)outWidth height:(GLsizei *)outHeight;
109 - (void)_destroyAGLContext;
110 - (void)_reshapeAGLWindow;
111 - (void)_hideAGLWindow;
112 - (NSImage *)_aglOffscreenImageForDrawingInRect:(NSRect)drawingInRect;
113 - (void)_redeliverStream;
116 static WebBaseNetscapePluginView *currentPluginView = nil;
118 typedef struct OpaquePortState* PortState;
120 static const double ThrottledTimerInterval = 0.25;
122 class PluginTimer : public TimerBase {
124 typedef void (*TimerFunc)(NPP npp, uint32 timerID);
126 PluginTimer(NPP npp, uint32 timerID, uint32 interval, NPBool repeat, TimerFunc timerFunc)
129 , m_interval(interval)
131 , m_timerFunc(timerFunc)
135 void start(bool throttle)
139 double timeInterval = throttle ? ThrottledTimerInterval : m_interval / 1000.0;
141 startRepeating(timeInterval);
143 startOneShot(timeInterval);
149 m_timerFunc(m_npp, m_timerID);
158 TimerFunc m_timerFunc;
161 #ifndef NP_NO_QUICKDRAW
163 // QuickDraw is not available in 64-bit
169 RgnHandle oldClipRegion;
170 RgnHandle oldVisibleRegion;
171 RgnHandle clipRegion;
175 #endif /* NP_NO_QUICKDRAW */
178 CGContextRef context;
182 AGLContext oldContext;
185 @class NSInputContext;
186 @interface NSResponder (IMSecretsIKnowAbout)
187 - (NSInputContext *)inputContext;
190 @interface WebPluginRequest : NSObject
192 NSURLRequest *_request;
193 NSString *_frameName;
195 BOOL _didStartFromUserGesture;
196 BOOL _sendNotification;
199 - (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture;
201 - (NSURLRequest *)request;
202 - (NSString *)frameName;
203 - (void *)notifyData;
204 - (BOOL)isCurrentEventUserGesture;
205 - (BOOL)sendNotification;
209 @interface NSData (WebPluginDataExtras)
210 - (BOOL)_web_startsWithBlankLine;
211 - (NSInteger)_web_locationAfterFirstBlankLine;
214 @interface WebBaseNetscapePluginView (ForwardDeclarations)
215 - (void)setWindowIfNecessary;
216 - (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification;
219 @implementation WebBaseNetscapePluginView
223 #ifndef BUILDING_ON_TIGER
224 WebCoreObjCFinalizeOnMainThread(self);
226 WKSendUserChangeNotifications();
231 - (BOOL)superviewsHaveSuperviews
233 NSView *contentView = [[self window] contentView];
235 for (view = self; view != nil; view = [view superview]) {
236 if (view == contentView) {
243 #ifndef NP_NO_QUICKDRAW
245 // The WindowRef created by -[NSWindow windowRef] has a QuickDraw GrafPort that covers
246 // the entire window frame (or structure region to use the Carbon term) rather then just the window content.
247 // We can remove this when <rdar://problem/4201099> is fixed.
248 - (void)fixWindowPort
250 ASSERT(drawingModel == NPDrawingModelQuickDraw);
252 NSWindow *currentWindow = [self currentWindow];
253 if ([currentWindow isKindOfClass:objc_getClass("NSCarbonWindow")])
256 float windowHeight = [currentWindow frame].size.height;
257 NSView *contentView = [currentWindow contentView];
258 NSRect contentRect = [contentView convertRect:[contentView frame] toView:nil]; // convert to window-relative coordinates
262 SetPort(GetWindowPort((WindowRef)[currentWindow windowRef]));
264 MovePortTo(static_cast<short>(contentRect.origin.x), /* Flip Y */ static_cast<short>(windowHeight - NSMaxY(contentRect)));
265 PortSize(static_cast<short>(contentRect.size.width), static_cast<short>(contentRect.size.height));
270 static UInt32 getQDPixelFormatForBitmapContext(CGContextRef context)
272 UInt32 byteOrder = CGBitmapContextGetBitmapInfo(context) & kCGBitmapByteOrderMask;
273 if (byteOrder == kCGBitmapByteOrderDefault)
274 switch (CGBitmapContextGetBitsPerPixel(context)) {
276 byteOrder = kCGBitmapByteOrder16Host;
279 byteOrder = kCGBitmapByteOrder32Host;
283 case kCGBitmapByteOrder16Little:
284 return k16LE555PixelFormat;
285 case kCGBitmapByteOrder32Little:
286 return k32BGRAPixelFormat;
287 case kCGBitmapByteOrder16Big:
288 return k16BE555PixelFormat;
289 case kCGBitmapByteOrder32Big:
290 return k32ARGBPixelFormat;
292 ASSERT_NOT_REACHED();
296 static inline void getNPRect(const CGRect& cgr, NPRect& npr)
298 npr.top = static_cast<uint16>(cgr.origin.y);
299 npr.left = static_cast<uint16>(cgr.origin.x);
300 npr.bottom = static_cast<uint16>(CGRectGetMaxY(cgr));
301 npr.right = static_cast<uint16>(CGRectGetMaxX(cgr));
306 static inline void getNPRect(const NSRect& nr, NPRect& npr)
308 npr.top = static_cast<uint16>(nr.origin.y);
309 npr.left = static_cast<uint16>(nr.origin.x);
310 npr.bottom = static_cast<uint16>(NSMaxY(nr));
311 npr.right = static_cast<uint16>(NSMaxX(nr));
314 - (NSRect)visibleRect
316 // WebCore may impose an additional clip (via CSS overflow or clip properties). Fetch
318 return NSIntersectionRect([self convertRect:[element _windowClipRect] fromView:nil], [super visibleRect]);
321 - (PortState)saveAndSetNewPortStateForUpdate:(BOOL)forUpdate
323 ASSERT([self currentWindow] != nil);
325 #ifndef NP_NO_QUICKDRAW
326 // If drawing with QuickDraw, fix the window port so that it has the same bounds as the NSWindow's
327 // content view. This makes it easier to convert between AppKit view and QuickDraw port coordinates.
328 if (drawingModel == NPDrawingModelQuickDraw)
329 [self fixWindowPort];
332 // Use AppKit to convert view coordinates to NSWindow coordinates.
333 NSRect boundsInWindow = [self convertRect:[self bounds] toView:nil];
334 NSRect visibleRectInWindow = [self convertRect:[self visibleRect] toView:nil];
336 // Flip Y to convert NSWindow coordinates to top-left-based window coordinates.
337 float borderViewHeight = [[self currentWindow] frame].size.height;
338 boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow);
339 visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow);
341 #ifndef NP_NO_QUICKDRAW
342 WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
345 // Look at the Carbon port to convert top-left-based window coordinates into top-left-based content coordinates.
346 if (drawingModel == NPDrawingModelQuickDraw) {
348 CGrafPtr port = GetWindowPort(windowRef);
349 GetPortBounds(port, &portBounds);
351 PixMap *pix = *GetPortPixMap(port);
352 boundsInWindow.origin.x += pix->bounds.left - portBounds.left;
353 boundsInWindow.origin.y += pix->bounds.top - portBounds.top;
354 visibleRectInWindow.origin.x += pix->bounds.left - portBounds.left;
355 visibleRectInWindow.origin.y += pix->bounds.top - portBounds.top;
359 window.x = (int32)boundsInWindow.origin.x;
360 window.y = (int32)boundsInWindow.origin.y;
361 window.width = static_cast<uint32>(NSWidth(boundsInWindow));
362 window.height = static_cast<uint32>(NSHeight(boundsInWindow));
364 // "Clip-out" the plug-in when:
365 // 1) it's not really in a window or off-screen or has no height or width.
366 // 2) window.x is a "big negative number" which is how WebCore expresses off-screen widgets.
367 // 3) the window is miniaturized or the app is hidden
368 // 4) we're inside of viewWillMoveToWindow: with a nil window. In this case, superviews may already have nil
369 // superviews and nil windows and results from convertRect:toView: are incorrect.
370 NSWindow *realWindow = [self window];
371 if (window.width <= 0 || window.height <= 0 || window.x < -100000
372 || realWindow == nil || [realWindow isMiniaturized]
374 || ![self superviewsHaveSuperviews]
375 || [self isHiddenOrHasHiddenAncestor]) {
377 // The following code tries to give plug-ins the same size they will eventually have.
378 // The specifiedWidth and specifiedHeight variables are used to predict the size that
379 // WebCore will eventually resize us to.
381 // The QuickTime plug-in has problems if you give it a width or height of 0.
382 // Since other plug-ins also might have the same sort of trouble, we make sure
383 // to always give plug-ins a size other than 0,0.
385 if (window.width <= 0)
386 window.width = specifiedWidth > 0 ? specifiedWidth : 100;
387 if (window.height <= 0)
388 window.height = specifiedHeight > 0 ? specifiedHeight : 100;
390 window.clipRect.bottom = window.clipRect.top;
391 window.clipRect.left = window.clipRect.right;
393 getNPRect(visibleRectInWindow, window.clipRect);
396 // Save the port state, set up the port for entry into the plugin
398 switch (drawingModel) {
399 #ifndef NP_NO_QUICKDRAW
400 case NPDrawingModelQuickDraw: {
403 CGrafPtr port = GetWindowPort(windowRef);
404 GetPortBounds(port, &portBounds);
405 nPort.qdPort.port = port;
406 nPort.qdPort.portx = (int32)-boundsInWindow.origin.x;
407 nPort.qdPort.porty = (int32)-boundsInWindow.origin.y;
408 window.window = &nPort;
410 PortState_QD *qdPortState = (PortState_QD*)malloc(sizeof(PortState_QD));
411 portState = (PortState)qdPortState;
413 GetGWorld(&qdPortState->oldPort, &qdPortState->oldDevice);
415 qdPortState->oldOrigin.h = portBounds.left;
416 qdPortState->oldOrigin.v = portBounds.top;
418 qdPortState->oldClipRegion = NewRgn();
419 GetPortClipRegion(port, qdPortState->oldClipRegion);
421 qdPortState->oldVisibleRegion = NewRgn();
422 GetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
424 RgnHandle clipRegion = NewRgn();
425 qdPortState->clipRegion = clipRegion;
427 CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
428 if (currentContext && WKCGContextIsBitmapContext(currentContext)) {
429 // We use WKCGContextIsBitmapContext here, because if we just called CGBitmapContextGetData
430 // on any context, we'd log to the console every time. But even if WKCGContextIsBitmapContext
431 // returns true, it still might not be a context we need to create a GWorld for; for example
432 // transparency layers will return true, but return 0 for CGBitmapContextGetData.
433 void* offscreenData = CGBitmapContextGetData(currentContext);
435 // If the current context is an offscreen bitmap, then create a GWorld for it.
436 ::Rect offscreenBounds;
437 offscreenBounds.top = 0;
438 offscreenBounds.left = 0;
439 offscreenBounds.right = CGBitmapContextGetWidth(currentContext);
440 offscreenBounds.bottom = CGBitmapContextGetHeight(currentContext);
441 GWorldPtr newOffscreenGWorld;
442 QDErr err = NewGWorldFromPtr(&newOffscreenGWorld,
443 getQDPixelFormatForBitmapContext(currentContext), &offscreenBounds, 0, 0, 0,
444 static_cast<char*>(offscreenData), CGBitmapContextGetBytesPerRow(currentContext));
445 ASSERT(newOffscreenGWorld && !err);
448 DisposeGWorld(offscreenGWorld);
449 offscreenGWorld = newOffscreenGWorld;
451 SetGWorld(offscreenGWorld, NULL);
453 port = offscreenGWorld;
455 nPort.qdPort.port = port;
456 boundsInWindow = [self bounds];
458 // Generate a QD origin based on the current affine transform for currentContext.
459 CGAffineTransform offscreenMatrix = CGContextGetCTM(currentContext);
460 CGPoint origin = {0,0};
461 CGPoint axisFlip = {1,1};
462 origin = CGPointApplyAffineTransform(origin, offscreenMatrix);
463 axisFlip = CGPointApplyAffineTransform(axisFlip, offscreenMatrix);
465 // Quartz bitmaps have origins at the bottom left, but the axes may be inverted, so handle that.
466 origin.x = offscreenBounds.left - origin.x * (axisFlip.x - origin.x);
467 origin.y = offscreenBounds.bottom + origin.y * (axisFlip.y - origin.y);
469 nPort.qdPort.portx = static_cast<int32>(-boundsInWindow.origin.x + origin.x);
470 nPort.qdPort.porty = static_cast<int32>(-boundsInWindow.origin.y - origin.y);
473 window.window = &nPort;
475 // Use the clip bounds from the context instead of the bounds we created
476 // from the window above.
477 getNPRect(CGRectOffset(CGContextGetClipBoundingBox(currentContext), -origin.x, origin.y), window.clipRect);
482 MacSetRectRgn(clipRegion,
483 window.clipRect.left + nPort.qdPort.portx, window.clipRect.top + nPort.qdPort.porty,
484 window.clipRect.right + nPort.qdPort.portx, window.clipRect.bottom + nPort.qdPort.porty);
486 // Clip to dirty region so plug-in does not draw over already-drawn regions of the window that are
487 // not going to be redrawn this update. This forces plug-ins to play nice with z-index ordering.
489 RgnHandle viewClipRegion = NewRgn();
491 // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
492 // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
493 // knows about the true set of dirty rects.
494 NSView *opaqueAncestor = [self opaqueAncestor];
495 const NSRect *dirtyRects;
496 NSInteger dirtyRectCount, dirtyRectIndex;
497 [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount];
499 for (dirtyRectIndex = 0; dirtyRectIndex < dirtyRectCount; dirtyRectIndex++) {
500 NSRect dirtyRect = [self convertRect:dirtyRects[dirtyRectIndex] fromView:opaqueAncestor];
501 if (!NSEqualSizes(dirtyRect.size, NSZeroSize)) {
502 // Create a region for this dirty rect
503 RgnHandle dirtyRectRegion = NewRgn();
504 SetRectRgn(dirtyRectRegion, static_cast<short>(NSMinX(dirtyRect)), static_cast<short>(NSMinY(dirtyRect)), static_cast<short>(NSMaxX(dirtyRect)), static_cast<short>(NSMaxY(dirtyRect)));
506 // Union this dirty rect with the rest of the dirty rects
507 UnionRgn(viewClipRegion, dirtyRectRegion, viewClipRegion);
508 DisposeRgn(dirtyRectRegion);
512 // Intersect the dirty region with the clip region, so that we only draw over dirty parts
513 SectRgn(clipRegion, viewClipRegion, clipRegion);
514 DisposeRgn(viewClipRegion);
517 // Switch to the port and set it up.
520 ForeColor(blackColor);
521 BackColor(whiteColor);
522 SetOrigin(nPort.qdPort.portx, nPort.qdPort.porty);
523 SetPortClipRegion(nPort.qdPort.port, clipRegion);
526 // AppKit may have tried to help us by doing a BeginUpdate.
527 // But the invalid region at that level didn't include AppKit's notion of what was not valid.
528 // We reset the port's visible region to counteract what BeginUpdate did.
529 SetPortVisibleRegion(nPort.qdPort.port, clipRegion);
530 InvalWindowRgn(windowRef, clipRegion);
533 qdPortState->forUpdate = forUpdate;
536 #endif /* NP_NO_QUICKDRAW */
538 case NPDrawingModelCoreGraphics: {
539 ASSERT([NSView focusView] == self);
541 CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
543 PortState_CG *cgPortState = (PortState_CG *)malloc(sizeof(PortState_CG));
544 portState = (PortState)cgPortState;
545 cgPortState->context = context;
547 // Update the plugin's window/context
549 nPort.cgPort.window = (NPNSWindow *)[self currentWindow];
551 nPort.cgPort.window = eventHandler->platformWindow([self currentWindow]);
552 #endif /* NP_NO_CARBON */
553 nPort.cgPort.context = context;
554 window.window = &nPort.cgPort;
556 // Save current graphics context's state; will be restored by -restorePortState:
557 CGContextSaveGState(context);
559 // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
560 // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
561 // knows about the true set of dirty rects.
562 NSView *opaqueAncestor = [self opaqueAncestor];
563 const NSRect *dirtyRects;
565 [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&count];
566 Vector<CGRect, 16> convertedDirtyRects;
567 convertedDirtyRects.resize(count);
568 for (int i = 0; i < count; ++i)
569 reinterpret_cast<NSRect&>(convertedDirtyRects[i]) = [self convertRect:dirtyRects[i] fromView:opaqueAncestor];
570 CGContextClipToRects(context, convertedDirtyRects.data(), count);
575 case NPDrawingModelOpenGL: {
576 ASSERT([NSView focusView] == self);
578 // Clear the "current" window and context -- they will be assigned below (if all goes well)
579 nPort.aglPort.window = NULL;
580 nPort.aglPort.context = NULL;
582 // Create AGL context if needed
583 if (![self _createAGLContextIfNeeded]) {
584 LOG_ERROR("Could not create AGL context");
588 // Update the plugin's window/context
590 nPort.aglPort.window = (NPNSWindow *)[self currentWindow];
592 nPort.aglPort.window = eventHandler->platformWindow([self currentWindow]);
593 #endif // NP_NO_CARBON
594 nPort.aglPort.context = [self _cglContext];
595 window.window = &nPort.aglPort;
597 // Save/set current AGL context
598 PortState_GL *glPortState = (PortState_GL *)malloc(sizeof(PortState_GL));
599 portState = (PortState)glPortState;
600 glPortState->oldContext = aglGetCurrentContext();
601 aglSetCurrentContext(aglContext);
603 // Adjust viewport according to clip
604 switch (window.type) {
605 case NPWindowTypeWindow:
606 glViewport(static_cast<GLint>(NSMinX(boundsInWindow) - NSMinX(visibleRectInWindow)),
607 static_cast<GLint>(NSMaxY(visibleRectInWindow) - NSMaxY(boundsInWindow)),
608 window.width, window.height);
611 case NPWindowTypeDrawable: {
612 GLsizei width, height;
613 if ([self _getAGLOffscreenBuffer:NULL width:&width height:&height])
614 glViewport(0, 0, width, height);
619 ASSERT_NOT_REACHED();
626 ASSERT_NOT_REACHED();
634 - (PortState)saveAndSetNewPortState
636 return [self saveAndSetNewPortStateForUpdate:NO];
639 - (void)restorePortState:(PortState)portState
641 ASSERT([self currentWindow]);
644 switch (drawingModel) {
645 #ifndef NP_NO_QUICKDRAW
646 case NPDrawingModelQuickDraw: {
647 PortState_QD *qdPortState = (PortState_QD *)portState;
648 WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
649 CGrafPtr port = GetWindowPort(windowRef);
653 if (qdPortState->forUpdate)
654 ValidWindowRgn(windowRef, qdPortState->clipRegion);
656 SetOrigin(qdPortState->oldOrigin.h, qdPortState->oldOrigin.v);
658 SetPortClipRegion(port, qdPortState->oldClipRegion);
659 if (qdPortState->forUpdate)
660 SetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
662 DisposeRgn(qdPortState->oldClipRegion);
663 DisposeRgn(qdPortState->oldVisibleRegion);
664 DisposeRgn(qdPortState->clipRegion);
666 SetGWorld(qdPortState->oldPort, qdPortState->oldDevice);
669 #endif /* NP_NO_QUICKDRAW */
671 case NPDrawingModelCoreGraphics:
672 ASSERT([NSView focusView] == self);
673 ASSERT(((PortState_CG *)portState)->context == nPort.cgPort.context);
674 CGContextRestoreGState(nPort.cgPort.context);
677 case NPDrawingModelOpenGL:
678 aglSetCurrentContext(((PortState_GL *)portState)->oldContext);
682 ASSERT_NOT_REACHED();
687 - (BOOL)sendEvent:(void*)event isDrawRect:(BOOL)eventIsDrawRect
696 ASSERT(NPP_HandleEvent);
698 // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
699 // We probably don't want more general reentrancy protection; we are really
700 // protecting only against this one case, which actually comes up when
701 // you first install the SVG viewer plug-in.
705 Frame* frame = core([self webFrame]);
708 Page* page = frame->page();
712 bool wasDeferring = page->defersLoading();
714 page->setDefersLoading(true);
716 // Can only send drawRect (updateEvt) to CoreGraphics and OpenGL plugins when actually drawing
717 ASSERT((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || !eventIsDrawRect || [NSView focusView] == self);
720 if ((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || eventIsDrawRect) {
721 // 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
722 // allowed to draw at any other time.
723 portState = [self saveAndSetNewPortStateForUpdate:eventIsDrawRect];
725 // We may have changed the window, so inform the plug-in.
726 [self setWindowIfNecessary];
730 #if !defined(NDEBUG) && !defined(NP_NO_QUICKDRAW)
731 // Draw green to help debug.
732 // If we see any green we know something's wrong.
733 // Note that PaintRect() only works for QuickDraw plugins; otherwise the current QD port is undefined.
734 if (drawingModel == NPDrawingModelQuickDraw && !isTransparent && eventIsDrawRect) {
735 ForeColor(greenColor);
736 const ::Rect bigRect = { -10000, -10000, 10000, 10000 };
738 ForeColor(blackColor);
742 // Temporarily retain self in case the plug-in view is released while sending an event.
743 [[self retain] autorelease];
746 [self willCallPlugInFunction];
748 KJS::JSLock::DropAllLocks dropAllLocks;
749 acceptedEvent = NPP_HandleEvent(plugin, event);
751 [self didCallPlugInFunction];
754 if ([self currentWindow])
755 [self restorePortState:portState];
760 page->setDefersLoading(false);
762 return acceptedEvent;
765 - (void)sendActivateEvent:(BOOL)activate
767 eventHandler->windowFocusChanged(activate);
770 - (void)sendDrawRectEvent:(NSRect)rect
772 eventHandler->drawRect(rect);
778 eventHandler->stopTimers();
780 shouldFireTimers = NO;
785 HashMap<uint32, PluginTimer*>::const_iterator end = timers->end();
786 for (HashMap<uint32, PluginTimer*>::const_iterator it = timers->begin(); it != end; ++it) {
787 PluginTimer* timer = it->second;
792 - (void)restartTimers
794 ASSERT([self window]);
796 if (shouldFireTimers)
799 if (!isStarted || [[self window] isMiniaturized])
802 shouldFireTimers = YES;
804 // If the plugin is completely obscured (scrolled out of view, for example), then we will
805 // send null events at a reduced rate.
806 eventHandler->startTimers(isCompletelyObscured);
811 HashMap<uint32, PluginTimer*>::const_iterator end = timers->end();
812 for (HashMap<uint32, PluginTimer*>::const_iterator it = timers->begin(); it != end; ++it) {
813 PluginTimer* timer = it->second;
814 ASSERT(!timer->isActive());
815 timer->start(isCompletelyObscured);
819 - (BOOL)acceptsFirstResponder
824 - (void)setHasFocus:(BOOL)flag
826 if (hasFocus == flag)
831 // We need to null check the event handler here because
832 // the plug-in view can resign focus after it's been stopped
833 // and the event handler has been deleted.
835 eventHandler->focusChanged(hasFocus);
838 - (BOOL)becomeFirstResponder
840 [self setHasFocus:YES];
844 - (BOOL)resignFirstResponder
846 [self setHasFocus:NO];
850 // AppKit doesn't call mouseDown or mouseUp on right-click. Simulate control-click
851 // mouseDown and mouseUp so plug-ins get the right-click event as they do in Carbon (3125743).
852 - (void)rightMouseDown:(NSEvent *)theEvent
854 [self mouseDown:theEvent];
857 - (void)rightMouseUp:(NSEvent *)theEvent
859 [self mouseUp:theEvent];
862 - (void)mouseDown:(NSEvent *)theEvent
864 eventHandler->mouseDown(theEvent);
867 - (void)mouseUp:(NSEvent *)theEvent
869 eventHandler->mouseUp(theEvent);
872 - (void)mouseEntered:(NSEvent *)theEvent
874 eventHandler->mouseEntered(theEvent);
877 - (void)mouseExited:(NSEvent *)theEvent
879 eventHandler->mouseExited(theEvent);
881 // Set cursor back to arrow cursor. Because NSCursor doesn't know about changes that the plugin made, we could get confused about what we think the
882 // current cursor is otherwise. Therefore we have no choice but to unconditionally reset the cursor when the mouse exits the plugin.
883 [[NSCursor arrowCursor] set];
886 // We can't name this method mouseMoved because we don't want to override
887 // the NSView mouseMoved implementation.
888 - (void)handleMouseMoved:(NSEvent *)theEvent
890 eventHandler->mouseMoved(theEvent);
893 - (void)mouseDragged:(NSEvent *)theEvent
895 eventHandler->mouseDragged(theEvent);
898 - (void)scrollWheel:(NSEvent *)theEvent
900 if (!eventHandler->scrollWheel(theEvent))
901 [super scrollWheel:theEvent];
904 - (void)keyUp:(NSEvent *)theEvent
906 eventHandler->keyUp(theEvent);
909 - (void)keyDown:(NSEvent *)theEvent
911 eventHandler->keyDown(theEvent);
914 - (void)flagsChanged:(NSEvent *)theEvent
916 eventHandler->flagsChanged(theEvent);
919 - (void)cut:(id)sender
921 eventHandler->keyDown([NSApp currentEvent]);
924 - (void)copy:(id)sender
926 eventHandler->keyDown([NSApp currentEvent]);
929 - (void)paste:(id)sender
931 eventHandler->keyDown([NSApp currentEvent]);
934 - (void)selectAll:(id)sender
936 eventHandler->keyDown([NSApp currentEvent]);
939 #pragma mark WEB_NETSCAPE_PLUGIN
941 - (BOOL)isNewWindowEqualToOldWindow
943 if (window.x != lastSetWindow.x)
945 if (window.y != lastSetWindow.y)
947 if (window.width != lastSetWindow.width)
949 if (window.height != lastSetWindow.height)
951 if (window.clipRect.top != lastSetWindow.clipRect.top)
953 if (window.clipRect.left != lastSetWindow.clipRect.left)
955 if (window.clipRect.bottom != lastSetWindow.clipRect.bottom)
957 if (window.clipRect.right != lastSetWindow.clipRect.right)
959 if (window.type != lastSetWindow.type)
962 switch (drawingModel) {
963 #ifndef NP_NO_QUICKDRAW
964 case NPDrawingModelQuickDraw:
965 if (nPort.qdPort.portx != lastSetPort.qdPort.portx)
967 if (nPort.qdPort.porty != lastSetPort.qdPort.porty)
969 if (nPort.qdPort.port != lastSetPort.qdPort.port)
972 #endif /* NP_NO_QUICKDRAW */
974 case NPDrawingModelCoreGraphics:
975 if (nPort.cgPort.window != lastSetPort.cgPort.window)
977 if (nPort.cgPort.context != lastSetPort.cgPort.context)
981 case NPDrawingModelOpenGL:
982 if (nPort.aglPort.window != lastSetPort.aglPort.window)
984 if (nPort.aglPort.context != lastSetPort.aglPort.context)
989 ASSERT_NOT_REACHED();
996 - (void)updateAndSetWindow
998 // A plug-in can only update if it's (1) already been started (2) isn't stopped
999 // and (3) is able to draw on-screen. To meet condition (3) the plug-in must not
1000 // be hidden and be attached to a window. QuickDraw plug-ins are an important
1001 // excpetion to rule (3) because they manually must be told when to stop writing
1002 // bits to the window backing store, thus to do so requires a new call to
1003 // NPP_SetWindow() with an empty NPWindow struct.
1006 #ifdef NP_NO_QUICKDRAW
1007 if (![self canDraw])
1010 if (drawingModel != NPDrawingModelQuickDraw && ![self canDraw])
1012 #endif // NP_NO_QUICKDRAW
1014 BOOL didLockFocus = [NSView focusView] != self && [self lockFocusIfCanDraw];
1015 PortState portState = [self saveAndSetNewPortState];
1017 [self setWindowIfNecessary];
1018 [self restorePortState:portState];
1025 - (void)setWindowIfNecessary
1031 if (![self isNewWindowEqualToOldWindow]) {
1032 // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
1033 // We probably don't want more general reentrancy protection; we are really
1034 // protecting only against this one case, which actually comes up when
1035 // you first install the SVG viewer plug-in.
1037 ASSERT(!inSetWindow);
1041 // A CoreGraphics or OpenGL plugin's window may only be set while the plugin is being updated
1042 ASSERT((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || [NSView focusView] == self);
1044 [self willCallPlugInFunction];
1046 KJS::JSLock::DropAllLocks dropAllLocks;
1047 npErr = NPP_SetWindow(plugin, &window);
1049 [self didCallPlugInFunction];
1053 switch (drawingModel) {
1054 #ifndef NP_NO_QUICKDRAW
1055 case NPDrawingModelQuickDraw:
1056 LOG(Plugins, "NPP_SetWindow (QuickDraw): %d, port=0x%08x, window.x:%d window.y:%d window.width:%d window.height:%d",
1057 npErr, (int)nPort.qdPort.port, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1059 #endif /* NP_NO_QUICKDRAW */
1061 case NPDrawingModelCoreGraphics:
1062 LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d",
1063 npErr, nPort.cgPort.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1066 case NPDrawingModelOpenGL:
1067 LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d",
1068 npErr, nPort.aglPort.window, nPort.aglPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1072 ASSERT_NOT_REACHED();
1075 #endif /* !defined(NDEBUG) */
1077 lastSetWindow = window;
1078 lastSetPort = nPort;
1082 - (void)removeTrackingRect
1085 [self removeTrackingRect:trackingTag];
1088 // Do the following after setting trackingTag to 0 so we don't re-enter.
1090 // Balance the retain in resetTrackingRect. Use autorelease in case we hold
1091 // the last reference to the window during tear-down, to avoid crashing AppKit.
1092 [[self window] autorelease];
1096 - (void)resetTrackingRect
1098 [self removeTrackingRect];
1100 // Retain the window so that removeTrackingRect can work after the window is closed.
1101 [[self window] retain];
1102 trackingTag = [self addTrackingRect:[self bounds] owner:self userData:nil assumeInside:NO];
1106 + (void)setCurrentPluginView:(WebBaseNetscapePluginView *)view
1108 currentPluginView = view;
1111 + (WebBaseNetscapePluginView *)currentPluginView
1113 return currentPluginView;
1123 if (_loadManually) {
1124 [self _redeliverStream];
1128 // If the OBJECT/EMBED tag has no SRC, the URL is passed to us as "".
1129 // Check for this and don't start a load in this case.
1130 if (sourceURL != nil && ![sourceURL _web_isEmpty]) {
1131 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:sourceURL];
1132 [request _web_setHTTPReferrer:core([self webFrame])->loader()->outgoingReferrer()];
1133 [self loadRequest:request inTarget:nil withNotifyData:nil sendNotification:NO];
1137 - (void)addWindowObservers
1139 ASSERT([self window]);
1141 NSWindow *theWindow = [self window];
1143 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
1144 [notificationCenter addObserver:self selector:@selector(windowWillClose:)
1145 name:NSWindowWillCloseNotification object:theWindow];
1146 [notificationCenter addObserver:self selector:@selector(windowBecameKey:)
1147 name:NSWindowDidBecomeKeyNotification object:theWindow];
1148 [notificationCenter addObserver:self selector:@selector(windowResignedKey:)
1149 name:NSWindowDidResignKeyNotification object:theWindow];
1150 [notificationCenter addObserver:self selector:@selector(windowDidMiniaturize:)
1151 name:NSWindowDidMiniaturizeNotification object:theWindow];
1152 [notificationCenter addObserver:self selector:@selector(windowDidDeminiaturize:)
1153 name:NSWindowDidDeminiaturizeNotification object:theWindow];
1155 [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchFromUser:)
1156 name:LoginWindowDidSwitchFromUserNotification object:nil];
1157 [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchToUser:)
1158 name:LoginWindowDidSwitchToUserNotification object:nil];
1161 - (void)removeWindowObservers
1163 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
1164 [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:nil];
1165 [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil];
1166 [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil];
1167 [notificationCenter removeObserver:self name:NSWindowDidMiniaturizeNotification object:nil];
1168 [notificationCenter removeObserver:self name:NSWindowDidDeminiaturizeNotification object:nil];
1169 [notificationCenter removeObserver:self name:LoginWindowDidSwitchFromUserNotification object:nil];
1170 [notificationCenter removeObserver:self name:LoginWindowDidSwitchToUserNotification object:nil];
1175 ASSERT([self currentWindow]);
1180 if (![self canStart])
1183 ASSERT([self webView]);
1185 if (![[[self webView] preferences] arePlugInsEnabled])
1188 // Open the plug-in package so it remains loaded while our plugin uses it
1189 [pluginPackage open];
1191 // Initialize drawingModel to an invalid value so that we can detect when the plugin does not specify a drawingModel
1192 drawingModel = (NPDrawingModel)-1;
1194 // Initialize eventModel to an invalid value so that we can detect when the plugin does not specify an event model.
1195 eventModel = (NPEventModel)-1;
1197 // Plug-ins are "windowed" by default. On MacOS, windowed plug-ins share the same window and graphics port as the main
1198 // browser window. Windowless plug-ins are rendered off-screen, then copied into the main browser window.
1199 window.type = NPWindowTypeWindow;
1201 NPError npErr = [self _createPlugin];
1202 if (npErr != NPERR_NO_ERROR) {
1203 LOG_ERROR("NPP_New failed with error: %d", npErr);
1204 [self _destroyPlugin];
1205 [pluginPackage close];
1209 if (drawingModel == (NPDrawingModel)-1) {
1210 #ifndef NP_NO_QUICKDRAW
1211 // Default to QuickDraw if the plugin did not specify a drawing model.
1212 drawingModel = NPDrawingModelQuickDraw;
1214 // QuickDraw is not available, so we can't default to it. Instead, default to CoreGraphics.
1215 drawingModel = NPDrawingModelCoreGraphics;
1219 if (eventModel == (NPEventModel)-1) {
1220 // If the plug-in did not specify a drawing model we default to Carbon when it is available.
1221 #ifndef NP_NO_CARBON
1222 eventModel = NPEventModelCarbon;
1224 eventModel = NPEventModelCocoa;
1225 #endif // NP_NO_CARBON
1228 #ifndef NP_NO_CARBON
1229 if (eventModel == NPEventModelCocoa &&
1230 drawingModel == NPDrawingModelQuickDraw) {
1231 LOG(Plugins, "Plugin can't use use Cocoa event model with QuickDraw drawing model: %@", pluginPackage);
1232 [self _destroyPlugin];
1233 [pluginPackage close];
1237 #endif // NP_NO_CARBON
1239 // Create the event handler
1240 eventHandler = WebNetscapePluginEventHandler::create(self);
1242 // Get the text input vtable
1243 if (eventModel == NPEventModelCocoa) {
1244 [self willCallPlugInFunction];
1246 KJS::JSLock::DropAllLocks dropAllLocks;
1247 NPPluginTextInputFuncs *value;
1248 if (NPP_GetValue(plugin, NPPVpluginTextInputFuncs, &value) == NPERR_NO_ERROR && value)
1249 textInputFuncs = value;
1251 [self didCallPlugInFunction];
1256 [self updateAndSetWindow];
1258 if ([self window]) {
1259 [self addWindowObservers];
1260 if ([[self window] isKeyWindow]) {
1261 [self sendActivateEvent:YES];
1263 [self restartTimers];
1266 [self resetTrackingRect];
1275 // If we're already calling a plug-in function, do not call NPP_Destroy(). The plug-in function we are calling
1276 // may assume that its instance->pdata, or other memory freed by NPP_Destroy(), is valid and unchanged until said
1277 // plugin-function returns.
1278 // See <rdar://problem/4480737>.
1279 if (pluginFunctionCallDepth > 0) {
1280 shouldStopSoon = YES;
1284 [self removeTrackingRect];
1290 // To stop active streams it's necessary to invoke makeObjectsPerformSelector on a copy
1291 // of streams. This is because calling -[WebNetscapePluginStream stop] also has the side effect
1292 // of removing a stream from this collection.
1293 NSArray *streamsCopy = [streams copy];
1294 [streamsCopy makeObjectsPerformSelector:@selector(stop)];
1295 [streamsCopy release];
1300 // Stop notifications and callbacks.
1301 [self removeWindowObservers];
1302 [[pendingFrameLoads allKeys] makeObjectsPerformSelector:@selector(_setInternalLoadDelegate:) withObject:nil];
1303 [NSObject cancelPreviousPerformRequestsWithTarget:self];
1305 // Setting the window type to 0 ensures that NPP_SetWindow will be called if the plug-in is restarted.
1306 lastSetWindow.type = (NPWindowType)0;
1308 [self _destroyPlugin];
1309 [pluginPackage close];
1311 delete eventHandler;
1316 if (drawingModel == NPDrawingModelOpenGL)
1317 [self _destroyAGLContext];
1325 - (NPEventModel)eventModel
1330 - (WebDataSource *)dataSource
1332 WebFrame *webFrame = kit(core(element)->document()->frame());
1333 return [webFrame _dataSource];
1336 - (WebFrame *)webFrame
1338 return [[self dataSource] webFrame];
1341 - (WebView *)webView
1343 return [[self webFrame] webView];
1346 - (NSWindow *)currentWindow
1348 return [self window] ? [self window] : [[self webView] hostWindow];
1356 - (WebNetscapePluginPackage *)pluginPackage
1358 return pluginPackage;
1361 - (void)setPluginPackage:(WebNetscapePluginPackage *)thePluginPackage;
1363 [thePluginPackage retain];
1364 [pluginPackage release];
1365 pluginPackage = thePluginPackage;
1367 NPP_New = [pluginPackage NPP_New];
1368 NPP_Destroy = [pluginPackage NPP_Destroy];
1369 NPP_SetWindow = [pluginPackage NPP_SetWindow];
1370 NPP_NewStream = [pluginPackage NPP_NewStream];
1371 NPP_WriteReady = [pluginPackage NPP_WriteReady];
1372 NPP_Write = [pluginPackage NPP_Write];
1373 NPP_StreamAsFile = [pluginPackage NPP_StreamAsFile];
1374 NPP_DestroyStream = [pluginPackage NPP_DestroyStream];
1375 NPP_HandleEvent = [pluginPackage NPP_HandleEvent];
1376 NPP_URLNotify = [pluginPackage NPP_URLNotify];
1377 NPP_GetValue = [pluginPackage NPP_GetValue];
1378 NPP_SetValue = [pluginPackage NPP_SetValue];
1379 NPP_Print = [pluginPackage NPP_Print];
1382 - (void)setMIMEType:(NSString *)theMIMEType
1384 NSString *type = [theMIMEType copy];
1389 - (void)setBaseURL:(NSURL *)theBaseURL
1391 [theBaseURL retain];
1393 baseURL = theBaseURL;
1396 - (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values;
1398 ASSERT([keys count] == [values count]);
1400 // Convert the attributes to 2 C string arrays.
1401 // These arrays are passed to NPP_New, but the strings need to be
1402 // modifiable and live the entire life of the plugin.
1404 // The Java plug-in requires the first argument to be the base URL
1405 if ([MIMEType isEqualToString:@"application/x-java-applet"]) {
1406 cAttributes = (char **)malloc(([keys count] + 1) * sizeof(char *));
1407 cValues = (char **)malloc(([values count] + 1) * sizeof(char *));
1408 cAttributes[0] = strdup("DOCBASE");
1409 cValues[0] = strdup([baseURL _web_URLCString]);
1412 cAttributes = (char **)malloc([keys count] * sizeof(char *));
1413 cValues = (char **)malloc([values count] * sizeof(char *));
1416 BOOL isWMP = [[[pluginPackage bundle] bundleIdentifier] isEqualToString:@"com.microsoft.WMP.defaultplugin"];
1419 unsigned count = [keys count];
1420 for (i = 0; i < count; i++) {
1421 NSString *key = [keys objectAtIndex:i];
1422 NSString *value = [values objectAtIndex:i];
1423 if ([key _webkit_isCaseInsensitiveEqualToString:@"height"]) {
1424 specifiedHeight = [value intValue];
1425 } else if ([key _webkit_isCaseInsensitiveEqualToString:@"width"]) {
1426 specifiedWidth = [value intValue];
1428 // Avoid Window Media Player crash when these attributes are present.
1429 if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) {
1432 cAttributes[argsCount] = strdup([key UTF8String]);
1433 cValues[argsCount] = strdup([value UTF8String]);
1434 LOG(Plugins, "%@ = %@", key, value);
1439 - (void)setMode:(int)theMode
1446 - (id)initWithFrame:(NSRect)frame
1447 pluginPackage:(WebNetscapePluginPackage *)thePluginPackage
1449 baseURL:(NSURL *)theBaseURL
1450 MIMEType:(NSString *)MIME
1451 attributeKeys:(NSArray *)keys
1452 attributeValues:(NSArray *)values
1453 loadManually:(BOOL)loadManually
1454 DOMElement:(DOMElement *)anElement
1456 [super initWithFrame:frame];
1458 streams = [[NSMutableArray alloc] init];
1459 pendingFrameLoads = [[NSMutableDictionary alloc] init];
1461 // load the plug-in if it is not already loaded
1462 if (![thePluginPackage load]) {
1466 [self setPluginPackage:thePluginPackage];
1468 element = [anElement retain];
1469 sourceURL = [theURL retain];
1471 [self setMIMEType:MIME];
1472 [self setBaseURL:theBaseURL];
1473 [self setAttributeKeys:keys andValues:values];
1475 [self setMode:NP_FULL];
1477 [self setMode:NP_EMBED];
1479 _loadManually = loadManually;
1484 - (id)initWithFrame:(NSRect)frame
1486 ASSERT_NOT_REACHED();
1492 #ifndef NP_NO_QUICKDRAW
1493 if (offscreenGWorld)
1494 DisposeGWorld(offscreenGWorld);
1498 for (i = 0; i < argsCount; i++) {
1499 free(cAttributes[i]);
1505 ASSERT(!eventHandler);
1508 deleteAllValues(*timers);
1513 - (void)disconnectStream:(WebBaseNetscapePluginStream*)stream
1515 [streams removeObjectIdenticalTo:stream];
1522 [sourceURL release];
1523 [_manualStream release];
1526 [pluginPackage release];
1530 [pendingFrameLoads release];
1535 ASSERT(!aglContext);
1544 ASSERT_MAIN_THREAD();
1552 - (void)drawRect:(NSRect)rect
1558 if ([NSGraphicsContext currentContextDrawingToScreen])
1559 [self sendDrawRectEvent:rect];
1561 NSBitmapImageRep *printedPluginBitmap = [self _printedPluginBitmap];
1562 if (printedPluginBitmap) {
1563 // Flip the bitmap before drawing because the QuickDraw port is flipped relative
1565 CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
1566 CGContextSaveGState(cgContext);
1567 NSRect bounds = [self bounds];
1568 CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
1569 CGContextScaleCTM(cgContext, 1.0f, -1.0f);
1570 [printedPluginBitmap drawInRect:bounds];
1571 CGContextRestoreGState(cgContext);
1575 // If this is a windowless OpenGL plugin, blit its contents back into this view. The plug-in just drew into the offscreen context.
1576 if (drawingModel == NPDrawingModelOpenGL && window.type == NPWindowTypeDrawable) {
1577 NSImage *aglOffscreenImage = [self _aglOffscreenImageForDrawingInRect:rect];
1578 if (aglOffscreenImage) {
1579 // Flip the context before drawing because the CGL context is flipped relative to this view.
1580 CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
1581 CGContextSaveGState(cgContext);
1582 NSRect bounds = [self bounds];
1583 CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
1584 CGContextScaleCTM(cgContext, 1.0f, -1.0f);
1586 // Copy 'rect' from the offscreen buffer to this view (the flip above makes this sort of tricky)
1587 NSRect flippedRect = rect;
1588 flippedRect.origin.y = NSMaxY(bounds) - NSMaxY(flippedRect);
1589 [aglOffscreenImage drawInRect:flippedRect fromRect:flippedRect operation:NSCompositeSourceOver fraction:1.0f];
1590 CGContextRestoreGState(cgContext);
1602 [super renewGState];
1604 // -renewGState is called whenever the view's geometry changes. It's a little hacky to override this method, but
1605 // much safer than walking up the view hierarchy and observing frame/bounds changed notifications, since you don't
1606 // have to track subsequent changes to the view hierarchy and add/remove notification observers.
1607 // NSOpenGLView uses the exact same technique to reshape its OpenGL surface.
1608 [self _viewHasMoved];
1611 #ifndef NP_NO_QUICKDRAW
1612 -(void)tellQuickTimeToChill
1614 ASSERT(drawingModel == NPDrawingModelQuickDraw);
1616 // Make a call to the secret QuickDraw API that makes QuickTime calm down.
1617 WindowRef windowRef = (WindowRef)[[self window] windowRef];
1621 CGrafPtr port = GetWindowPort(windowRef);
1623 GetPortBounds(port, &bounds);
1624 WKCallDrawingNotification(port, &bounds);
1626 #endif /* NP_NO_QUICKDRAW */
1628 - (void)viewWillMoveToWindow:(NSWindow *)newWindow
1630 #ifndef NP_NO_QUICKDRAW
1631 if (drawingModel == NPDrawingModelQuickDraw)
1632 [self tellQuickTimeToChill];
1635 // We must remove the tracking rect before we move to the new window.
1636 // Once we move to the new window, it will be too late.
1637 [self removeTrackingRect];
1638 [self removeWindowObservers];
1640 // Workaround for: <rdar://problem/3822871> resignFirstResponder is not sent to first responder view when it is removed from the window
1641 [self setHasFocus:NO];
1644 // Hide the AGL child window
1645 if (drawingModel == NPDrawingModelOpenGL)
1646 [self _hideAGLWindow];
1648 if ([[self webView] hostWindow]) {
1649 // View will be moved out of the actual window but it still has a host window.
1652 // View will have no associated windows.
1655 // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
1656 // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
1657 [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1662 - (void)viewWillMoveToSuperview:(NSView *)newSuperview
1664 if (!newSuperview) {
1665 // Stop the plug-in when it is removed from its superview. It is not sufficient to do this in -viewWillMoveToWindow:nil, because
1666 // the WebView might still has a hostWindow at that point, which prevents the plug-in from being destroyed.
1667 // There is no need to start the plug-in when moving into a superview. -viewDidMoveToWindow takes care of that.
1670 // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
1671 // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
1672 [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1676 - (void)viewDidMoveToWindow
1678 [self resetTrackingRect];
1680 if ([self window]) {
1681 // While in the view hierarchy, observe WebPreferencesChangedNotification so that we can start/stop depending
1682 // on whether plugins are enabled.
1683 [[NSNotificationCenter defaultCenter] addObserver:self
1684 selector:@selector(preferencesHaveChanged:)
1685 name:WebPreferencesChangedNotification
1688 // View moved to an actual window. Start it if not already started.
1690 [self restartTimers];
1691 [self addWindowObservers];
1692 } else if ([[self webView] hostWindow]) {
1693 // View moved out of an actual window, but still has a host window.
1694 // Call setWindow to explicitly "clip out" the plug-in from sight.
1695 // FIXME: It would be nice to do this where we call stopNullEvents in viewWillMoveToWindow.
1696 [self updateAndSetWindow];
1700 - (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
1702 if (!hostWindow && ![self window]) {
1703 // View will have no associated windows.
1706 // Remove WebPreferencesChangedNotification observer -- we will observe once again when we move back into the window
1707 [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
1711 - (void)viewDidMoveToHostWindow
1713 if ([[self webView] hostWindow]) {
1714 // View now has an associated window. Start it if not already started.
1719 #pragma mark NOTIFICATIONS
1721 - (void)windowWillClose:(NSNotification *)notification
1726 - (void)windowBecameKey:(NSNotification *)notification
1728 [self sendActivateEvent:YES];
1729 [self setNeedsDisplay:YES];
1730 [self restartTimers];
1731 #ifndef NP_NO_CARBON
1732 SetUserFocusWindow((WindowRef)[[self window] windowRef]);
1733 #endif // NP_NO_CARBON
1736 - (void)windowResignedKey:(NSNotification *)notification
1738 [self sendActivateEvent:NO];
1739 [self setNeedsDisplay:YES];
1740 [self restartTimers];
1743 - (void)windowDidMiniaturize:(NSNotification *)notification
1748 - (void)windowDidDeminiaturize:(NSNotification *)notification
1753 - (void)loginWindowDidSwitchFromUser:(NSNotification *)notification
1758 -(void)loginWindowDidSwitchToUser:(NSNotification *)notification
1760 [self restartTimers];
1763 - (void)preferencesHaveChanged:(NSNotification *)notification
1765 WebPreferences *preferences = [[self webView] preferences];
1766 BOOL arePlugInsEnabled = [preferences arePlugInsEnabled];
1768 if ([notification object] == preferences && isStarted != arePlugInsEnabled) {
1769 if (arePlugInsEnabled) {
1770 if ([self currentWindow]) {
1775 [self setNeedsDisplay:YES];
1780 - (NPObject *)createPluginScriptableObject
1782 if (!NPP_GetValue || ![self isStarted])
1785 NPObject *value = NULL;
1787 [self willCallPlugInFunction];
1789 KJS::JSLock::DropAllLocks dropAllLocks;
1790 error = NPP_GetValue(plugin, NPPVpluginScriptableNPObject, &value);
1792 [self didCallPlugInFunction];
1793 if (error != NPERR_NO_ERROR)
1799 - (void)willCallPlugInFunction
1803 // Could try to prevent infinite recursion here, but it's probably not worth the effort.
1804 pluginFunctionCallDepth++;
1807 - (void)didCallPlugInFunction
1809 ASSERT(pluginFunctionCallDepth > 0);
1810 pluginFunctionCallDepth--;
1812 // If -stop was called while we were calling into a plug-in function, and we're no longer
1813 // inside a plug-in function, stop now.
1814 if (pluginFunctionCallDepth == 0 && shouldStopSoon) {
1815 shouldStopSoon = NO;
1820 -(void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
1822 ASSERT(_loadManually);
1823 ASSERT(!_manualStream);
1825 _manualStream = [[WebNetscapePluginStream alloc] initWithFrameLoader:core([self webFrame])->loader()];
1828 - (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
1830 ASSERT(_loadManually);
1831 ASSERT(_manualStream);
1833 _dataLengthReceived += [data length];
1835 if (![self isStarted])
1838 if ([_manualStream plugin] == NULL) {
1839 [_manualStream setRequestURL:[[[self dataSource] request] URL]];
1840 [_manualStream setPlugin:[self plugin]];
1841 ASSERT([_manualStream plugin]);
1842 [_manualStream startStreamWithResponse:[[self dataSource] response]];
1845 if ([_manualStream plugin])
1846 [_manualStream receivedData:data];
1849 - (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
1851 ASSERT(_loadManually);
1857 if (![self isStarted]) {
1861 [_manualStream destroyStreamWithError:error];
1864 - (void)pluginViewFinishedLoading:(NSView *)pluginView
1866 ASSERT(_loadManually);
1867 ASSERT(_manualStream);
1869 if ([self isStarted])
1870 [_manualStream finishedLoading];
1873 #pragma mark NSTextInput implementation
1875 - (NSInputContext *)inputContext
1877 if ([self isStarted] && eventModel == NPEventModelCarbon)
1880 return [super inputContext];
1883 - (BOOL)hasMarkedText
1885 ASSERT(eventModel == NPEventModelCocoa);
1886 ASSERT([self isStarted]);
1888 if (textInputFuncs && textInputFuncs->hasMarkedText)
1889 return textInputFuncs->hasMarkedText(plugin);
1894 - (void)insertText:(id)aString
1896 ASSERT(eventModel == NPEventModelCocoa);
1897 ASSERT([self isStarted]);
1899 if (textInputFuncs && textInputFuncs->insertText)
1900 textInputFuncs->insertText(plugin, aString);
1903 - (NSRange)markedRange
1905 ASSERT(eventModel == NPEventModelCocoa);
1906 ASSERT([self isStarted]);
1908 if (textInputFuncs && textInputFuncs->markedRange)
1909 return textInputFuncs->markedRange(plugin);
1911 return NSMakeRange(NSNotFound, 0);
1914 - (NSRange)selectedRange
1916 ASSERT(eventModel == NPEventModelCocoa);
1917 ASSERT([self isStarted]);
1919 if (textInputFuncs && textInputFuncs->selectedRange)
1920 return textInputFuncs->selectedRange(plugin);
1922 return NSMakeRange(NSNotFound, 0);
1925 - (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange
1927 ASSERT(eventModel == NPEventModelCocoa);
1928 ASSERT([self isStarted]);
1930 if (textInputFuncs && textInputFuncs->setMarkedText)
1931 textInputFuncs->setMarkedText(plugin, aString, selRange);
1936 ASSERT(eventModel == NPEventModelCocoa);
1937 ASSERT([self isStarted]);
1939 if (textInputFuncs && textInputFuncs->unmarkText)
1940 textInputFuncs->unmarkText(plugin);
1943 - (NSArray *)validAttributesForMarkedText
1945 ASSERT(eventModel == NPEventModelCocoa);
1946 ASSERT([self isStarted]);
1948 if (textInputFuncs && textInputFuncs->validAttributesForMarkedText)
1949 return textInputFuncs->validAttributesForMarkedText(plugin);
1951 return [NSArray array];
1954 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange
1956 ASSERT(eventModel == NPEventModelCocoa);
1957 ASSERT([self isStarted]);
1959 if (textInputFuncs && textInputFuncs->attributedSubstringFromRange)
1960 return textInputFuncs->attributedSubstringFromRange(plugin, theRange);
1965 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
1967 ASSERT(eventModel == NPEventModelCocoa);
1968 ASSERT([self isStarted]);
1970 if (textInputFuncs && textInputFuncs->characterIndexForPoint) {
1971 // Convert the point to window coordinates
1972 NSPoint point = [[self window] convertScreenToBase:thePoint];
1974 // And view coordinates
1975 point = [self convertPoint:point fromView:nil];
1977 return textInputFuncs->characterIndexForPoint(plugin, point);
1983 - (void)doCommandBySelector:(SEL)aSelector
1985 ASSERT(eventModel == NPEventModelCocoa);
1986 ASSERT([self isStarted]);
1988 if (textInputFuncs && textInputFuncs->doCommandBySelector)
1989 textInputFuncs->doCommandBySelector(plugin, aSelector);
1992 - (NSRect)firstRectForCharacterRange:(NSRange)theRange
1994 ASSERT(eventModel == NPEventModelCocoa);
1995 ASSERT([self isStarted]);
1997 if (textInputFuncs && textInputFuncs->firstRectForCharacterRange) {
1998 NSRect rect = textInputFuncs->firstRectForCharacterRange(plugin, theRange);
2000 // Convert the rect to window coordinates
2001 rect = [self convertRect:rect toView:nil];
2003 // Convert the rect location to screen coordinates
2004 rect.origin = [[self window] convertBaseToScreen:rect.origin];
2012 // test for 10.4 because of <rdar://problem/4243463>
2013 #ifdef BUILDING_ON_TIGER
2014 - (long)conversationIdentifier
2019 - (NSInteger)conversationIdentifier
2021 return (NSInteger)self;
2027 @implementation WebBaseNetscapePluginView (WebNPPCallbacks)
2029 - (NSMutableURLRequest *)requestWithURLCString:(const char *)URLCString
2034 CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, URLCString, kCFStringEncodingISOLatin1);
2035 ASSERT(string); // All strings should be representable in ISO Latin 1
2037 NSString *URLString = [(NSString *)string _web_stringByStrippingReturnCharacters];
2038 NSURL *URL = [NSURL _web_URLWithDataAsString:URLString relativeToURL:baseURL];
2043 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
2044 Frame* frame = core([self webFrame]);
2047 [request _web_setHTTPReferrer:frame->loader()->outgoingReferrer()];
2051 - (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest
2053 // FIXME: Is this isStarted check needed here? evaluateJavaScriptPluginRequest should not be called
2054 // if we are stopped since this method is called after a delay and we call
2055 // cancelPreviousPerformRequestsWithTarget inside of stop.
2060 NSURL *URL = [[JSPluginRequest request] URL];
2061 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
2064 NSString *result = [[self webFrame] _stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]];
2066 // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop.
2071 if ([JSPluginRequest frameName] != nil) {
2072 // FIXME: If the result is a string, we probably want to put that string into the frame.
2073 if ([JSPluginRequest sendNotification]) {
2074 [self willCallPlugInFunction];
2076 KJS::JSLock::DropAllLocks dropAllLocks;
2077 NPP_URLNotify(plugin, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]);
2079 [self didCallPlugInFunction];
2081 } else if ([result length] > 0) {
2082 // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does.
2083 NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding];
2084 WebBaseNetscapePluginStream *stream = [[WebBaseNetscapePluginStream alloc] initWithRequestURL:URL
2086 notifyData:[JSPluginRequest notifyData]
2087 sendNotification:[JSPluginRequest sendNotification]];
2088 [stream startStreamResponseURL:URL
2089 expectedContentLength:[JSData length]
2090 lastModifiedDate:nil
2091 MIMEType:@"text/plain"
2093 [stream receivedData:JSData];
2094 [stream finishedLoading];
2099 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
2103 WebPluginRequest *pluginRequest = [pendingFrameLoads objectForKey:webFrame];
2104 ASSERT(pluginRequest != nil);
2105 ASSERT([pluginRequest sendNotification]);
2107 [self willCallPlugInFunction];
2109 KJS::JSLock::DropAllLocks dropAllLocks;
2110 NPP_URLNotify(plugin, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]);
2112 [self didCallPlugInFunction];
2114 [pendingFrameLoads removeObjectForKey:webFrame];
2115 [webFrame _setInternalLoadDelegate:nil];
2118 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
2120 NPReason reason = NPRES_DONE;
2122 reason = [WebBaseNetscapePluginStream reasonForError:error];
2124 [self webFrame:webFrame didFinishLoadWithReason:reason];
2127 - (void)loadPluginRequest:(WebPluginRequest *)pluginRequest
2129 NSURLRequest *request = [pluginRequest request];
2130 NSString *frameName = [pluginRequest frameName];
2131 WebFrame *frame = nil;
2133 NSURL *URL = [request URL];
2134 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
2136 ASSERT(frameName || JSString);
2139 // FIXME - need to get rid of this window creation which
2140 // bypasses normal targeted link handling
2141 frame = kit(core([self webFrame])->loader()->findFrameForNavigation(frameName));
2143 WebView *currentWebView = [self webView];
2144 NSDictionary *features = [[NSDictionary alloc] init];
2145 WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView
2146 createWebViewWithRequest:nil
2147 windowFeatures:features];
2151 if ([pluginRequest sendNotification]) {
2152 [self willCallPlugInFunction];
2154 KJS::JSLock::DropAllLocks dropAllLocks;
2155 NPP_URLNotify(plugin, [[[pluginRequest request] URL] _web_URLCString], NPERR_GENERIC_ERROR, [pluginRequest notifyData]);
2157 [self didCallPlugInFunction];
2162 frame = [newWebView mainFrame];
2163 core(frame)->tree()->setName(frameName);
2164 [[newWebView _UIDelegateForwarder] webViewShow:newWebView];
2169 ASSERT(frame == nil || [self webFrame] == frame);
2170 [self evaluateJavaScriptPluginRequest:pluginRequest];
2172 [frame loadRequest:request];
2173 if ([pluginRequest sendNotification]) {
2174 // Check if another plug-in view or even this view is waiting for the frame to load.
2175 // If it is, tell it that the load was cancelled because it will be anyway.
2176 WebBaseNetscapePluginView *view = [frame _internalLoadDelegate];
2178 ASSERT([view isKindOfClass:[WebBaseNetscapePluginView class]]);
2179 [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK];
2181 [pendingFrameLoads _webkit_setObject:pluginRequest forUncopiedKey:frame];
2182 [frame _setInternalLoadDelegate:self];
2187 - (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification
2189 NSURL *URL = [request URL];
2192 return NPERR_INVALID_URL;
2194 NSString *target = nil;
2196 // Find the frame given the target string.
2197 target = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, cTarget, kCFStringEncodingWindowsLatin1);
2199 WebFrame *frame = [self webFrame];
2201 // don't let a plugin start any loads if it is no longer part of a document that is being
2202 // displayed unless the loads are in the same frame as the plugin.
2203 if ([[self dataSource] _documentLoader] != core([self webFrame])->loader()->activeDocumentLoader() &&
2204 (!cTarget || [frame findFrameNamed:target] != frame)) {
2207 return NPERR_GENERIC_ERROR;
2210 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
2211 if (JSString != nil) {
2212 if (![[[self webView] preferences] isJavaScriptEnabled]) {
2213 // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does.
2214 return NPERR_GENERIC_ERROR;
2215 } else if (cTarget == NULL && mode == NP_FULL) {
2216 // Don't allow a JavaScript request from a standalone plug-in that is self-targetted
2217 // because this can cause the user to be redirected to a blank page (3424039).
2218 return NPERR_INVALID_PARAM;
2222 if (cTarget || JSString) {
2223 // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't
2224 // want to potentially kill the plug-in inside of its URL request.
2226 if (JSString != nil && target != nil && [frame findFrameNamed:target] != frame) {
2227 // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
2229 return NPERR_INVALID_PARAM;
2232 bool currentEventIsUserGesture = false;
2234 currentEventIsUserGesture = eventHandler->currentEventIsUserGesture();
2236 WebPluginRequest *pluginRequest = [[WebPluginRequest alloc] initWithRequest:request
2238 notifyData:notifyData
2239 sendNotification:sendNotification
2240 didStartFromUserGesture:currentEventIsUserGesture];
2241 [self performSelector:@selector(loadPluginRequest:) withObject:pluginRequest afterDelay:0];
2242 [pluginRequest release];
2246 WebNetscapePluginStream *stream = [[WebNetscapePluginStream alloc] initWithRequest:request
2248 notifyData:notifyData
2249 sendNotification:sendNotification];
2251 return NPERR_INVALID_URL;
2253 [streams addObject:stream];
2258 return NPERR_NO_ERROR;
2261 -(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData
2263 LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget);
2265 NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
2266 return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES];
2269 -(NPError)getURL:(const char *)URLCString target:(const char *)cTarget
2271 LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget);
2273 NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
2274 return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO];
2277 - (NPError)_postURL:(const char *)URLCString
2278 target:(const char *)target
2280 buf:(const char *)buf
2282 notifyData:(void *)notifyData
2283 sendNotification:(BOOL)sendNotification
2284 allowHeaders:(BOOL)allowHeaders
2286 if (!URLCString || !len || !buf) {
2287 return NPERR_INVALID_PARAM;
2290 NSData *postData = nil;
2293 // If we're posting a file, buf is either a file URL or a path to the file.
2294 NSString *bufString = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingWindowsLatin1);
2296 return NPERR_INVALID_PARAM;
2298 NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString];
2300 if ([fileURL isFileURL]) {
2301 path = [fileURL path];
2305 postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]];
2306 CFRelease(bufString);
2308 return NPERR_FILE_NOT_FOUND;
2311 postData = [NSData dataWithBytes:buf length:len];
2314 if ([postData length] == 0) {
2315 return NPERR_INVALID_PARAM;
2318 NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
2319 [request setHTTPMethod:@"POST"];
2322 if ([postData _web_startsWithBlankLine]) {
2323 postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)];
2325 NSInteger location = [postData _web_locationAfterFirstBlankLine];
2326 if (location != NSNotFound) {
2327 // If the blank line is somewhere in the middle of postData, everything before is the header.
2328 NSData *headerData = [postData subdataWithRange:NSMakeRange(0, location)];
2329 NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields];
2330 unsigned dataLength = [postData length] - location;
2332 // Sometimes plugins like to set Content-Length themselves when they post,
2333 // but WebFoundation does not like that. So we will remove the header
2334 // and instead truncate the data to the requested length.
2335 NSString *contentLength = [header objectForKey:@"Content-Length"];
2337 if (contentLength != nil)
2338 dataLength = MIN((unsigned)[contentLength intValue], dataLength);
2339 [header removeObjectForKey:@"Content-Length"];
2341 if ([header count] > 0) {
2342 [request setAllHTTPHeaderFields:header];
2344 // Everything after the blank line is the actual content of the POST.
2345 postData = [postData subdataWithRange:NSMakeRange(location, dataLength)];
2349 if ([postData length] == 0) {
2350 return NPERR_INVALID_PARAM;
2354 // Plug-ins expect to receive uncached data when doing a POST (3347134).
2355 [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
2356 [request setHTTPBody:postData];
2358 return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification];
2361 - (NPError)postURLNotify:(const char *)URLCString
2362 target:(const char *)target
2364 buf:(const char *)buf
2366 notifyData:(void *)notifyData
2368 LOG(Plugins, "NPN_PostURLNotify: %s", URLCString);
2369 return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:notifyData sendNotification:YES allowHeaders:YES];
2372 -(NPError)postURL:(const char *)URLCString
2373 target:(const char *)target
2375 buf:(const char *)buf
2378 LOG(Plugins, "NPN_PostURL: %s", URLCString);
2379 // As documented, only allow headers to be specified via NPP_PostURL when using a file.
2380 return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:NULL sendNotification:NO allowHeaders:file];
2383 -(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream
2385 LOG(Plugins, "NPN_NewStream");
2386 return NPERR_GENERIC_ERROR;
2389 -(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer
2391 LOG(Plugins, "NPN_Write");
2392 return NPERR_GENERIC_ERROR;
2395 -(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason
2397 LOG(Plugins, "NPN_DestroyStream");
2398 // This function does a sanity check to ensure that the NPStream provided actually
2399 // belongs to the plug-in that provided it, which fixes a crash in the DivX
2400 // plug-in: <rdar://problem/5093862> | http://bugs.webkit.org/show_bug.cgi?id=13203
2401 if (!stream || [WebBaseNetscapePluginStream ownerForStream:stream] != plugin) {
2402 LOG(Plugins, "Invalid NPStream passed to NPN_DestroyStream: %p", stream);
2403 return NPERR_INVALID_INSTANCE_ERROR;
2406 WebBaseNetscapePluginStream *browserStream = static_cast<WebBaseNetscapePluginStream *>(stream->ndata);
2407 [browserStream cancelLoadAndDestroyStreamWithError:[browserStream errorForReason:reason]];
2409 return NPERR_NO_ERROR;
2412 - (const char *)userAgent
2414 return [[[self webView] userAgentForURL:baseURL] UTF8String];
2417 -(void)status:(const char *)message
2420 LOG_ERROR("NPN_Status passed a NULL status message");
2424 CFStringRef status = CFStringCreateWithCString(NULL, message, kCFStringEncodingUTF8);
2426 LOG_ERROR("NPN_Status: the message was not valid UTF-8");
2430 LOG(Plugins, "NPN_Status: %@", status);
2431 WebView *wv = [self webView];
2432 [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status];
2436 -(void)invalidateRect:(NPRect *)invalidRect
2438 LOG(Plugins, "NPN_InvalidateRect");
2439 [self setNeedsDisplayInRect:NSMakeRect(invalidRect->left, invalidRect->top,
2440 (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)];
2448 - (void)invalidateRegion:(NPRegion)invalidRegion
2450 LOG(Plugins, "NPN_InvalidateRegion");
2451 NSRect invalidRect = NSZeroRect;
2452 switch (drawingModel) {
2453 #ifndef NP_NO_QUICKDRAW
2454 case NPDrawingModelQuickDraw:
2457 GetRegionBounds((NPQDRegion)invalidRegion, &qdRect);
2458 invalidRect = NSMakeRect(qdRect.left, qdRect.top, qdRect.right - qdRect.left, qdRect.bottom - qdRect.top);
2461 #endif /* NP_NO_QUICKDRAW */
2463 case NPDrawingModelCoreGraphics:
2464 case NPDrawingModelOpenGL:
2466 CGRect cgRect = CGPathGetBoundingBox((NPCGRegion)invalidRegion);
2467 invalidRect = *(NSRect *)&cgRect;
2472 ASSERT_NOT_REACHED();
2476 [self setNeedsDisplayInRect:invalidRect];
2481 LOG(Plugins, "forceRedraw");
2482 [self setNeedsDisplay:YES];
2483 [[self window] displayIfNeeded];
2486 static NPBrowserTextInputFuncs *browserTextInputFuncs()
2488 static NPBrowserTextInputFuncs inputFuncs = {
2490 sizeof(NPBrowserTextInputFuncs),
2491 NPN_MarkedTextAbandoned,
2492 NPN_MarkedTextSelectionChanged
2498 - (NPError)getVariable:(NPNVariable)variable value:(void *)value
2501 case NPNVWindowNPObject:
2503 Frame* frame = core([self webFrame]);
2504 NPObject* windowScriptObject = frame ? frame->windowScriptNPObject() : 0;
2506 // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
2507 if (windowScriptObject)
2508 _NPN_RetainObject(windowScriptObject);
2510 void **v = (void **)value;
2511 *v = windowScriptObject;
2513 return NPERR_NO_ERROR;
2516 case NPNVPluginElementNPObject:
2519 return NPERR_GENERIC_ERROR;
2521 NPObject *plugInScriptObject = (NPObject *)[element _NPObject];
2523 // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
2524 if (plugInScriptObject)
2525 _NPN_RetainObject(plugInScriptObject);
2527 void **v = (void **)value;
2528 *v = plugInScriptObject;
2530 return NPERR_NO_ERROR;
2533 case NPNVpluginDrawingModel:
2535 *(NPDrawingModel *)value = drawingModel;
2536 return NPERR_NO_ERROR;
2539 #ifndef NP_NO_QUICKDRAW
2540 case NPNVsupportsQuickDrawBool:
2542 *(NPBool *)value = TRUE;
2543 return NPERR_NO_ERROR;
2545 #endif /* NP_NO_QUICKDRAW */
2547 case NPNVsupportsCoreGraphicsBool:
2549 *(NPBool *)value = TRUE;
2550 return NPERR_NO_ERROR;
2553 case NPNVsupportsOpenGLBool:
2555 *(NPBool *)value = TRUE;
2556 return NPERR_NO_ERROR;
2559 case NPNVpluginEventModel:
2561 *(NPEventModel *)value = eventModel;
2562 return NPERR_NO_ERROR;
2565 #ifndef NP_NO_CARBON
2566 case NPNVsupportsCarbonBool:
2568 *(NPBool *)value = TRUE;
2569 return NPERR_NO_ERROR;
2571 #endif /* NP_NO_CARBON */
2573 case NPNVsupportsCocoaBool:
2575 *(NPBool *)value = TRUE;
2576 return NPERR_NO_ERROR;
2579 case NPNVbrowserTextInputFuncs:
2581 if (eventModel == NPEventModelCocoa) {
2582 *(NPBrowserTextInputFuncs **)value = browserTextInputFuncs();
2583 return NPERR_NO_ERROR;
2590 return NPERR_GENERIC_ERROR;
2593 - (NPError)setVariable:(NPPVariable)variable value:(void *)value
2596 case NPPVpluginWindowBool:
2598 NPWindowType newWindowType = (value ? NPWindowTypeWindow : NPWindowTypeDrawable);
2600 // Redisplay if window type is changing (some drawing models can only have their windows set while updating).
2601 if (newWindowType != window.type)
2602 [self setNeedsDisplay:YES];
2604 window.type = newWindowType;
2607 case NPPVpluginTransparentBool:
2609 BOOL newTransparent = (value != 0);
2611 // Redisplay if transparency is changing
2612 if (isTransparent != newTransparent)
2613 [self setNeedsDisplay:YES];
2615 isTransparent = newTransparent;
2617 return NPERR_NO_ERROR;
2620 case NPPVpluginDrawingModel:
2622 // Can only set drawing model inside NPP_New()
2623 if (self != [[self class] currentPluginView])
2624 return NPERR_GENERIC_ERROR;
2626 // Check for valid, supported drawing model
2627 NPDrawingModel newDrawingModel = (NPDrawingModel)(uintptr_t)value;
2628 switch (newDrawingModel) {
2629 // Supported drawing models:
2630 #ifndef NP_NO_QUICKDRAW
2631 case NPDrawingModelQuickDraw:
2633 case NPDrawingModelCoreGraphics:
2634 case NPDrawingModelOpenGL:
2635 drawingModel = newDrawingModel;
2636 return NPERR_NO_ERROR;
2638 // Unsupported (or unknown) drawing models:
2640 LOG(Plugins, "Plugin %@ uses unsupported drawing model: %d", pluginPackage, drawingModel);
2641 return NPERR_GENERIC_ERROR;
2645 case NPPVpluginEventModel:
2647 // Can only set event model inside NPP_New()
2648 if (self != [[self class] currentPluginView])
2649 return NPERR_GENERIC_ERROR;
2651 // Check for valid, supported event model
2652 NPEventModel newEventModel = (NPEventModel)(uintptr_t)value;
2653 switch (newEventModel) {
2654 // Supported event models:
2655 #ifndef NP_NO_CARBON
2656 case NPEventModelCarbon:
2658 case NPEventModelCocoa:
2659 eventModel = newEventModel;
2660 return NPERR_NO_ERROR;
2662 // Unsupported (or unknown) event models:
2664 LOG(Plugins, "Plugin %@ uses unsupported event model: %d", pluginPackage, eventModel);
2665 return NPERR_GENERIC_ERROR;
2670 return NPERR_GENERIC_ERROR;
2674 - (uint32)scheduleTimerWithInterval:(uint32)interval repeat:(NPBool)repeat timerFunc:(void (*)(NPP npp, uint32 timerID))timerFunc
2680 timers = new HashMap<uint32, PluginTimer*>;
2682 uint32 timerID = ++currentTimerID;
2684 PluginTimer* timer = new PluginTimer(plugin, timerID, interval, repeat, timerFunc);
2685 timers->set(timerID, timer);
2687 if (shouldFireTimers)
2688 timer->start(isCompletelyObscured);
2693 - (void)unscheduleTimer:(uint32)timerID
2698 if (PluginTimer* timer = timers->take(timerID))
2702 - (NPError)popUpContextMenu:(NPMenu *)menu
2704 NSEvent *currentEvent = [NSApp currentEvent];
2706 // NPN_PopUpContextMenu must be called from within the plug-in's NPP_HandleEvent.
2708 return NPERR_GENERIC_ERROR;
2710 [NSMenu popUpContextMenu:(NSMenu *)menu withEvent:currentEvent forView:self];
2711 return NPERR_NO_ERROR;
2716 @implementation WebPluginRequest
2718 - (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture
2721 _didStartFromUserGesture = currentEventIsUserGesture;
2722 _request = [request retain];
2723 _frameName = [frameName retain];
2724 _notifyData = notifyData;
2725 _sendNotification = sendNotification;
2732 [_frameName release];
2736 - (NSURLRequest *)request
2741 - (NSString *)frameName
2746 - (BOOL)isCurrentEventUserGesture
2748 return _didStartFromUserGesture;
2751 - (BOOL)sendNotification
2753 return _sendNotification;
2756 - (void *)notifyData
2763 @implementation WebBaseNetscapePluginView (Internal)
2765 - (NPError)_createPlugin
2767 plugin = (NPP)calloc(1, sizeof(NPP_t));
2768 plugin->ndata = self;
2772 // NPN_New(), which creates the plug-in instance, should never be called while calling a plug-in function for that instance.
2773 ASSERT(pluginFunctionCallDepth == 0);
2775 [[self class] setCurrentPluginView:self];
2776 NPError npErr = NPP_New((char *)[MIMEType cString], plugin, mode, argsCount, cAttributes, cValues, NULL);
2777 [[self class] setCurrentPluginView:nil];
2779 LOG(Plugins, "NPP_New: %d", npErr);
2783 - (void)_destroyPlugin
2786 npErr = NPP_Destroy(plugin, NULL);
2787 LOG(Plugins, "NPP_Destroy: %d", npErr);
2789 if (Frame* frame = core([self webFrame]))
2790 frame->cleanupScriptObjectsForPlugin(self);
2796 - (void)_viewHasMoved
2798 // All of the work this method does may safely be skipped if the view is not in a window. When the view
2799 // is moved back into a window, everything should be set up correctly.
2803 if (drawingModel == NPDrawingModelOpenGL)
2804 [self _reshapeAGLWindow];
2806 #ifndef NP_NO_QUICKDRAW
2807 if (drawingModel == NPDrawingModelQuickDraw)
2808 [self tellQuickTimeToChill];
2810 [self updateAndSetWindow];
2811 [self resetTrackingRect];
2813 // Check to see if the plugin view is completely obscured (scrolled out of view, for example).
2814 // For performance reasons, we send null events at a lower rate to plugins which are obscured.
2815 BOOL oldIsObscured = isCompletelyObscured;
2816 isCompletelyObscured = NSIsEmptyRect([self visibleRect]);
2817 if (isCompletelyObscured != oldIsObscured)
2818 [self restartTimers];
2821 - (NSBitmapImageRep *)_printedPluginBitmap
2823 #ifdef NP_NO_QUICKDRAW
2826 // Cannot print plugins that do not implement NPP_Print
2830 // This NSBitmapImageRep will share its bitmap buffer with a GWorld that the plugin will draw into.
2831 // The bitmap is created in 32-bits-per-pixel ARGB format, which is the default GWorld pixel format.
2832 NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
2833 pixelsWide:window.width
2834 pixelsHigh:window.height
2839 colorSpaceName:NSDeviceRGBColorSpace
2840 bitmapFormat:NSAlphaFirstBitmapFormat
2842 bitsPerPixel:0] autorelease];
2845 // Create a GWorld with the same underlying buffer into which the plugin can draw
2846 ::Rect printGWorldBounds;
2847 SetRect(&printGWorldBounds, 0, 0, window.width, window.height);
2848 GWorldPtr printGWorld;
2849 if (NewGWorldFromPtr(&printGWorld,
2855 (Ptr)[bitmap bitmapData],
2856 [bitmap bytesPerRow]) != noErr) {
2857 LOG_ERROR("Could not create GWorld for printing");
2861 /// Create NPWindow for the GWorld
2862 NPWindow printNPWindow;
2863 printNPWindow.window = &printGWorld; // Normally this is an NP_Port, but when printing it is the actual CGrafPtr
2864 printNPWindow.x = 0;
2865 printNPWindow.y = 0;
2866 printNPWindow.width = window.width;
2867 printNPWindow.height = window.height;
2868 printNPWindow.clipRect.top = 0;
2869 printNPWindow.clipRect.left = 0;
2870 printNPWindow.clipRect.right = window.width;
2871 printNPWindow.clipRect.bottom = window.height;
2872 printNPWindow.type = NPWindowTypeDrawable; // Offscreen graphics port as opposed to a proper window
2874 // Create embed-mode NPPrint
2876 npPrint.mode = NP_EMBED;
2877 npPrint.print.embedPrint.window = printNPWindow;
2878 npPrint.print.embedPrint.platformPrint = printGWorld;
2880 // Tell the plugin to print into the GWorld
2881 [self willCallPlugInFunction];
2883 KJS::JSLock::DropAllLocks dropAllLocks;
2884 NPP_Print(plugin, &npPrint);
2886 [self didCallPlugInFunction];
2888 // Don't need the GWorld anymore
2889 DisposeGWorld(printGWorld);
2895 - (BOOL)_createAGLContextIfNeeded
2897 ASSERT(drawingModel == NPDrawingModelOpenGL);
2899 // Do nothing (but indicate success) if the AGL context already exists
2903 switch (window.type) {
2904 case NPWindowTypeWindow:
2905 return [self _createWindowedAGLContext];
2907 case NPWindowTypeDrawable:
2908 return [self _createWindowlessAGLContext];
2911 ASSERT_NOT_REACHED();
2916 - (BOOL)_createWindowedAGLContext
2918 ASSERT(drawingModel == NPDrawingModelOpenGL);
2919 ASSERT(!aglContext);
2921 ASSERT([self window]);
2923 GLint pixelFormatAttributes[] = {
2935 // Choose AGL pixel format
2936 AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL, 0, pixelFormatAttributes);
2938 LOG_ERROR("Could not find suitable AGL pixel format: %s", aglErrorString(aglGetError()));
2942 // Create AGL context
2943 aglContext = aglCreateContext(pixelFormat, NULL);
2944 aglDestroyPixelFormat(pixelFormat);
2946 LOG_ERROR("Could not create AGL context: %s", aglErrorString(aglGetError()));
2950 // Create AGL window
2951 aglWindow = [[NSWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
2953 LOG_ERROR("Could not create window for AGL drawable.");
2957 // AGL window should allow clicks to go through -- mouse events are tracked by WebCore
2958 [aglWindow setIgnoresMouseEvents:YES];
2960 // Make sure the window is not opaque -- windowed plug-ins cannot layer with other page elements
2961 [aglWindow setOpaque:YES];
2963 // Position and order in the AGL window
2964 [self _reshapeAGLWindow];
2966 // Attach the AGL context to its window
2968 #ifdef AGL_VERSION_3_0
2969 success = aglSetWindowRef(aglContext, (WindowRef)[aglWindow windowRef]);
2971 success = aglSetDrawable(aglContext, (AGLDrawable)GetWindowPort((WindowRef)[aglWindow windowRef]));
2974 LOG_ERROR("Could not set AGL drawable: %s", aglErrorString(aglGetError()));
2975 aglDestroyContext(aglContext);
2983 - (BOOL)_createWindowlessAGLContext
2985 ASSERT(drawingModel == NPDrawingModelOpenGL);
2986 ASSERT(!aglContext);
2989 GLint pixelFormatAttributes[] = {
3000 // Choose AGL pixel format
3001 AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL, 0, pixelFormatAttributes);
3003 LOG_ERROR("Could not find suitable AGL pixel format: %s", aglErrorString(aglGetError()));
3007 // Create AGL context
3008 aglContext = aglCreateContext(pixelFormat, NULL);
3009 aglDestroyPixelFormat(pixelFormat);
3011 LOG_ERROR("Could not create AGL context: %s", aglErrorString(aglGetError()));
3015 // Create offscreen buffer for AGL context
3016 NSSize boundsSize = [self bounds].size;
3017 GLvoid *offscreenBuffer = (GLvoid *)malloc(static_cast<size_t>(boundsSize.width * boundsSize.height * 4));
3018 if (!offscreenBuffer) {
3019 LOG_ERROR("Could not allocate offscreen buffer for AGL context");
3020 aglDestroyContext(aglContext);
3025 // Attach AGL context to offscreen buffer
3026 CGLContextObj cglContext = [self _cglContext];
3027 CGLError error = CGLSetOffScreen(cglContext, static_cast<long>(boundsSize.width), static_cast<long>(boundsSize.height), static_cast<long>(boundsSize.width * 4), offscreenBuffer);
3029 LOG_ERROR("Could not set offscreen buffer for AGL context: %d", error);
3030 aglDestroyContext(aglContext);
3038 - (CGLContextObj)_cglContext
3040 ASSERT(drawingModel == NPDrawingModelOpenGL);
3042 CGLContextObj cglContext = NULL;
3043 if (!aglGetCGLContext(aglContext, (void **)&cglContext) || !cglContext)
3044 LOG_ERROR("Could not get CGL context for AGL context: %s", aglErrorString(aglGetError()));
3049 - (BOOL)_getAGLOffscreenBuffer:(GLvoid **)outBuffer width:(GLsizei *)outWidth height:(GLsizei *)outHeight
3051 ASSERT(drawingModel == NPDrawingModelOpenGL);
3060 // Only windowless plug-ins have offscreen buffers
3061 if (window.type != NPWindowTypeDrawable)
3064 CGLContextObj cglContext = [self _cglContext];
3068 GLsizei width, height;
3070 void *offscreenBuffer = NULL;
3071 CGLError error = CGLGetOffScreen(cglContext, &width, &height, &rowBytes, &offscreenBuffer);
3072 if (error || !offscreenBuffer) {
3073 LOG_ERROR("Could not get offscreen buffer for AGL context: %d", error);
3078 *outBuffer = offscreenBuffer;
3082 *outHeight = height;
3087 - (void)_destroyAGLContext
3089 ASSERT(drawingModel == NPDrawingModelOpenGL);
3095 // If this is a windowless plug-in, free its offscreen buffer
3096 GLvoid *offscreenBuffer;
3097 if ([self _getAGLOffscreenBuffer:&offscreenBuffer width:NULL height:NULL])
3098 free(offscreenBuffer);
3100 // Detach context from the AGL window
3101 #ifdef AGL_VERSION_3_0
3102 aglSetWindowRef(aglContext, NULL);
3104 aglSetDrawable(aglContext, NULL);
3107 // Destroy the context
3108 aglDestroyContext(aglContext);
3112 // Destroy the AGL window
3114 [self _hideAGLWindow];
3119 - (void)_reshapeAGLWindow
3121 ASSERT(drawingModel == NPDrawingModelOpenGL);
3126 switch (window.type) {
3127 case NPWindowTypeWindow:
3132 // The AGL window is being reshaped because the plugin view has moved. Since the view has moved, it will soon redraw.
3133 // We want the AGL window to update at the same time as its underlying view. So, we disable screen updates until the
3134 // plugin view's window flushes.
3135 NSWindow *browserWindow = [self window];
3136 ASSERT(browserWindow);
3137 [browserWindow disableScreenUpdatesUntilFlush];
3139 // Add the AGL window as a child of the main window if necessary
3140 if ([aglWindow parentWindow] != browserWindow)
3141 [browserWindow addChildWindow:aglWindow ordered:NSWindowAbove];
3143 // Update the AGL window frame
3144 NSRect aglWindowFrame = [self convertRect:[self visibleRect] toView:nil];
3145 aglWindowFrame.origin = [browserWindow convertBaseToScreen:aglWindowFrame.origin];
3146 [aglWindow setFrame:aglWindowFrame display:NO];
3148 // Update the AGL context
3149 aglUpdateContext(aglContext);
3153 case NPWindowTypeDrawable:
3155 // Get offscreen buffer; we can skip this step if we don't have one yet
3156 GLvoid *offscreenBuffer;
3157 GLsizei width, height;
3158 if (![self _getAGLOffscreenBuffer:&offscreenBuffer width:&width height:&height] || !offscreenBuffer)
3161 // Don't resize the offscreen buffer if it's already the same size as the view bounds
3162 NSSize boundsSize = [self bounds].size;
3163 if (boundsSize.width == width && boundsSize.height == height)
3166 // Resize the offscreen buffer
3167 offscreenBuffer = realloc(offscreenBuffer, static_cast<size_t>(boundsSize.width * boundsSize.height * 4));
3168 if (!offscreenBuffer) {
3169 LOG_ERROR("Could not allocate offscreen buffer for AGL context");
3173 // Update the offscreen
3174 CGLContextObj cglContext = [self _cglContext];
3175 CGLError error = CGLSetOffScreen(cglContext, static_cast<long>(boundsSize.width), static_cast<long>(boundsSize.height), static_cast<long>(boundsSize.width * 4), offscreenBuffer);
3177 LOG_ERROR("Could not set offscreen buffer for AGL context: %d", error);
3181 // Update the AGL context
3182 aglUpdateContext(aglContext);
3187 ASSERT_NOT_REACHED();
3192 - (void)_hideAGLWindow
3194 ASSERT(drawingModel == NPDrawingModelOpenGL);
3199 // aglWindow should only be set for a windowed OpenGL plug-in
3200 ASSERT(window.type == NPWindowTypeWindow);
3202 NSWindow *parentWindow = [aglWindow parentWindow];
3204 // Disable screen updates so that this AGL window orders out atomically with other plugins' AGL windows
3205 [parentWindow disableScreenUpdatesUntilFlush];
3206 ASSERT(parentWindow == [self window]);
3207 [parentWindow removeChildWindow:aglWindow];
3209 [aglWindow orderOut:nil];
3212 - (NSImage *)_aglOffscreenImageForDrawingInRect:(NSRect)drawingInRect
3214 ASSERT(drawingModel == NPDrawingModelOpenGL);
3216 CGLContextObj cglContext = [self _cglContext];
3220 // Get the offscreen buffer
3221 GLvoid *offscreenBuffer;
3222 GLsizei width, height;
3223 if (![self _getAGLOffscreenBuffer:&offscreenBuffer width:&width height:&height])
3226 unsigned char *plane = (unsigned char *)offscreenBuffer;
3228 #if defined(__i386__) || defined(__x86_64__)
3229 // Make rect inside the offscreen buffer because we're about to directly modify the bits inside drawingInRect
3230 NSRect rect = NSIntegralRect(NSIntersectionRect(drawingInRect, NSMakeRect(0, 0, width, height)));
3232 // The offscreen buffer, being an OpenGL framebuffer, is in BGRA format on x86. We need to swap the blue and red channels before
3233 // wrapping the buffer in an NSBitmapImageRep, which only supports RGBA and ARGB.
3234 // On PowerPC, the OpenGL framebuffer is in ARGB format. Since that is a format that NSBitmapImageRep supports, all that is
3235 // needed on PowerPC is to pass the NSAlphaFirstBitmapFormat flag when creating the NSBitmapImageRep. On x86, we need to swap the
3236 // framebuffer color components such that they are in ARGB order, as they are on PowerPC.
3237 // 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.
3238 // Since we know what region of the image will ultimately be drawn to screen (drawingInRect), we restrict the channel swapping to
3239 // just that region within the offscreen buffer.
3240 if (!WebConvertBGRAToARGB(plane, width * 4, (int)rect.origin.x, (int)rect.origin.y, (int)rect.size.width, (int)rect.size.height))
3242 #endif /* defined(__i386__) || defined(__x86_64__) */
3244 NSBitmapImageRep *aglBitmap = [[NSBitmapImageRep alloc]
3245 initWithBitmapDataPlanes:&plane
3252 colorSpaceName:NSDeviceRGBColorSpace
3253 bitmapFormat:NSAlphaFirstBitmapFormat
3254 bytesPerRow:width * 4
3257 LOG_ERROR("Could not create bitmap for AGL offscreen buffer");
3261 // Wrap the bitmap in an NSImage. This allocation isn't very expensive -- the actual image data is already in the bitmap rep
3262 NSImage *aglImage = [[[NSImage alloc] initWithSize:[aglBitmap size]] autorelease];
3263 [aglImage addRepresentation:aglBitmap];
3264 [aglBitmap release];
3269 - (void)_redeliverStream
3271 if ([self dataSource] && [self isStarted]) {
3272 // Deliver what has not been passed to the plug-in up to this point.
3273 if (_dataLengthReceived > 0) {
3274 NSData *data = [[[self dataSource] data] subdataWithRange:NSMakeRange(0, _dataLengthReceived)];
3275 _dataLengthReceived = 0;
3276 [self pluginView:self receivedData:data];
3277 if (![[self dataSource] isLoading]) {
3279 [self pluginView:self receivedError:_error];
3281 [self pluginViewFinishedLoading:self];
3289 @implementation NSData (PluginExtras)
3291 - (BOOL)_web_startsWithBlankLine
3293 return [self length] > 0 && ((const char *)[self bytes])[0] == '\n';
3297 - (NSInteger)_web_locationAfterFirstBlankLine
3299 const char *bytes = (const char *)[self bytes];
3300 unsigned length = [self length];
3303 for (i = 0; i < length - 4; i++) {
3305 // Support for Acrobat. It sends "\n\n".
3306 if (bytes[i] == '\n' && bytes[i+1] == '\n') {
3310 // Returns the position after 2 CRLF's or 1 CRLF if it is the first line.
3311 if (bytes[i] == '\r' && bytes[i+1] == '\n') {
3315 } else if (bytes[i] == '\n') {
3316 // Support for Director. It sends "\r\n\n" (3880387).
3318 } else if (bytes[i] == '\r' && bytes[i+1] == '\n') {
3319 // Support for Flash. It sends "\r\n\r\n" (3758113).