1/*
2 * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#if ENABLE(NETSCAPE_PLUGIN_API)
30
31#import "WebNetscapePluginView.h"
32
33#import "QuickDrawCompatibility.h"
34#import "WebDataSourceInternal.h"
35#import "WebDefaultUIDelegate.h"
36#import "WebFrameInternal.h"
37#import "WebFrameView.h"
38#import "WebKitErrorsPrivate.h"
39#import "WebKitLogging.h"
40#import "WebKitNSStringExtras.h"
41#import "WebKitSystemInterface.h"
42#import "WebNSDataExtras.h"
43#import "WebNSDictionaryExtras.h"
44#import "WebNSObjectExtras.h"
45#import "WebNSURLExtras.h"
46#import "WebNSURLRequestExtras.h"
47#import "WebNSViewExtras.h"
48#import "WebNetscapeContainerCheckContextInfo.h"
49#import "WebNetscapeContainerCheckPrivate.h"
50#import "WebNetscapePluginEventHandler.h"
51#import "WebNetscapePluginPackage.h"
52#import "WebNetscapePluginStream.h"
53#import "WebPluginContainerCheck.h"
54#import "WebPluginRequest.h"
55#import "WebPreferences.h"
56#import "WebUIDelegatePrivate.h"
57#import "WebViewInternal.h"
58#import <Carbon/Carbon.h>
59#import <WebCore/CookieJar.h>
60#import <WebCore/DocumentLoader.h>
61#import <WebCore/Element.h>
62#import <WebCore/Frame.h>
63#import <WebCore/FrameLoader.h>
64#import <WebCore/FrameTree.h>
65#import <WebCore/FrameView.h>
66#import <WebCore/HTMLPlugInElement.h>
67#import <WebCore/Page.h>
68#import <WebCore/PluginMainThreadScheduler.h>
69#import <WebCore/ProxyServer.h>
70#import <WebCore/ScriptController.h>
71#import <WebCore/SecurityOrigin.h>
72#import <WebCore/SoftLinking.h>
73#import <WebCore/WebCoreObjCExtras.h>
74#import <WebCore/WebCoreURLResponse.h>
75#import <WebCore/npruntime_impl.h>
76#import <WebKit/DOMPrivate.h>
77#import <WebKit/WebUIDelegate.h>
78#import <objc/objc-runtime.h>
79#import <runtime/InitializeThreading.h>
80#import <runtime/JSLock.h>
81#import <wtf/Assertions.h>
82#import <wtf/Threading.h>
83#import <wtf/text/CString.h>
84
85#define LoginWindowDidSwitchFromUserNotification    @"WebLoginWindowDidSwitchFromUserNotification"
86#define LoginWindowDidSwitchToUserNotification      @"WebLoginWindowDidSwitchToUserNotification"
87#define WKNVSupportsCompositingCoreAnimationPluginsBool 74656  /* TRUE if the browser supports hardware compositing of Core Animation plug-ins  */
88static const int WKNVSilverlightFullscreenPerformanceIssueFixed = 7288546; /* TRUE if Siverlight addressed its underlying  bug in <rdar://problem/7288546> */
89
90using namespace WebCore;
91using namespace WebKit;
92using namespace std;
93
94static inline bool isDrawingModelQuickDraw(NPDrawingModel drawingModel)
95{
96#ifndef NP_NO_QUICKDRAW
97    return drawingModel == NPDrawingModelQuickDraw;
98#else
99    return false;
100#endif
101};
102
103@interface WebNetscapePluginView (Internal)
104- (NPError)_createPlugin;
105- (void)_destroyPlugin;
106- (NSBitmapImageRep *)_printedPluginBitmap;
107- (void)_redeliverStream;
108- (BOOL)_shouldCancelSrcStream;
109@end
110
111static WebNetscapePluginView *currentPluginView = nil;
112
113typedef struct OpaquePortState* PortState;
114
115static const double ThrottledTimerInterval = 0.25;
116
117class PluginTimer : public TimerBase {
118public:
119    typedef void (*TimerFunc)(NPP npp, uint32_t timerID);
120
121    PluginTimer(NPP npp, uint32_t timerID, uint32_t interval, NPBool repeat, TimerFunc timerFunc)
122        : m_npp(npp)
123        , m_timerID(timerID)
124        , m_interval(interval)
125        , m_repeat(repeat)
126        , m_timerFunc(timerFunc)
127    {
128    }
129
130    void start(bool throttle)
131    {
132        ASSERT(!isActive());
133
134        double timeInterval = m_interval / 1000.0;
135
136        if (throttle)
137            timeInterval = max(timeInterval, ThrottledTimerInterval);
138
139        if (m_repeat)
140            startRepeating(timeInterval);
141        else
142            startOneShot(timeInterval);
143    }
144
145private:
146    virtual void fired()
147    {
148        m_timerFunc(m_npp, m_timerID);
149        if (!m_repeat)
150            delete this;
151    }
152
153    NPP m_npp;
154    uint32_t m_timerID;
155    uint32_t m_interval;
156    NPBool m_repeat;
157    TimerFunc m_timerFunc;
158};
159
160#ifndef NP_NO_QUICKDRAW
161
162// QuickDraw is not available in 64-bit
163
164typedef struct {
165    GrafPtr oldPort;
166    GDHandle oldDevice;
167    Point oldOrigin;
168    RgnHandle oldClipRegion;
169    RgnHandle oldVisibleRegion;
170    RgnHandle clipRegion;
171    BOOL forUpdate;
172} PortState_QD;
173
174#endif /* NP_NO_QUICKDRAW */
175
176typedef struct {
177    CGContextRef context;
178} PortState_CG;
179
180@class NSTextInputContext;
181@interface NSResponder (AppKitDetails)
182- (NSTextInputContext *)inputContext;
183@end
184
185@interface WebNetscapePluginView (ForwardDeclarations)
186- (void)setWindowIfNecessary;
187- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification;
188@end
189
190@implementation WebNetscapePluginView
191
192+ (void)initialize
193{
194    JSC::initializeThreading();
195    WTF::initializeMainThreadToProcessMainThread();
196#ifndef BUILDING_ON_TIGER
197    WebCoreObjCFinalizeOnMainThread(self);
198#endif
199    WKSendUserChangeNotifications();
200}
201
202// MARK: EVENTS
203
204// The WindowRef created by -[NSWindow windowRef] has a QuickDraw GrafPort that covers
205// the entire window frame (or structure region to use the Carbon term) rather then just the window content.
206// We can remove this when <rdar://problem/4201099> is fixed.
207- (void)fixWindowPort
208{
209#ifndef NP_NO_QUICKDRAW
210    ASSERT(isDrawingModelQuickDraw(drawingModel));
211
212    NSWindow *currentWindow = [self currentWindow];
213    if ([currentWindow isKindOfClass:objc_getClass("NSCarbonWindow")])
214        return;
215
216    float windowHeight = [currentWindow frame].size.height;
217    NSView *contentView = [currentWindow contentView];
218    NSRect contentRect = [contentView convertRect:[contentView frame] toView:nil]; // convert to window-relative coordinates
219
220    CGrafPtr oldPort;
221    GetPort(&oldPort);
222    SetPort(GetWindowPort((WindowRef)[currentWindow windowRef]));
223
224    MovePortTo(static_cast<short>(contentRect.origin.x), /* Flip Y */ static_cast<short>(windowHeight - NSMaxY(contentRect)));
225    PortSize(static_cast<short>(contentRect.size.width), static_cast<short>(contentRect.size.height));
226
227    SetPort(oldPort);
228#endif
229}
230
231#ifndef NP_NO_QUICKDRAW
232static UInt32 getQDPixelFormatForBitmapContext(CGContextRef context)
233{
234    UInt32 byteOrder = CGBitmapContextGetBitmapInfo(context) & kCGBitmapByteOrderMask;
235    if (byteOrder == kCGBitmapByteOrderDefault)
236        switch (CGBitmapContextGetBitsPerPixel(context)) {
237            case 16:
238                byteOrder = kCGBitmapByteOrder16Host;
239                break;
240            case 32:
241                byteOrder = kCGBitmapByteOrder32Host;
242                break;
243        }
244    switch (byteOrder) {
245        case kCGBitmapByteOrder16Little:
246            return k16LE555PixelFormat;
247        case kCGBitmapByteOrder32Little:
248            return k32BGRAPixelFormat;
249        case kCGBitmapByteOrder16Big:
250            return k16BE555PixelFormat;
251        case kCGBitmapByteOrder32Big:
252            return k32ARGBPixelFormat;
253    }
254    ASSERT_NOT_REACHED();
255    return 0;
256}
257
258static inline void getNPRect(const CGRect& cgr, NPRect& npr)
259{
260    npr.top = static_cast<uint16_t>(cgr.origin.y);
261    npr.left = static_cast<uint16_t>(cgr.origin.x);
262    npr.bottom = static_cast<uint16_t>(CGRectGetMaxY(cgr));
263    npr.right = static_cast<uint16_t>(CGRectGetMaxX(cgr));
264}
265
266#endif
267
268static inline void getNPRect(const NSRect& nr, NPRect& npr)
269{
270    npr.top = static_cast<uint16_t>(nr.origin.y);
271    npr.left = static_cast<uint16_t>(nr.origin.x);
272    npr.bottom = static_cast<uint16_t>(NSMaxY(nr));
273    npr.right = static_cast<uint16_t>(NSMaxX(nr));
274}
275
276- (PortState)saveAndSetNewPortStateForUpdate:(BOOL)forUpdate
277{
278    ASSERT([self currentWindow] != nil);
279
280    // The base coordinates of a window and it's contentView happen to be the equal at a userSpaceScaleFactor
281    // of 1. For non-1.0 scale factors this assumption is false.
282    NSView *windowContentView = [[self window] contentView];
283    NSRect boundsInWindow = [self convertRect:[self bounds] toView:windowContentView];
284    NSRect visibleRectInWindow = [self actualVisibleRectInWindow];
285
286    // Flip Y to convert -[NSWindow contentView] coordinates to top-left-based window coordinates.
287    float borderViewHeight = [[self currentWindow] frame].size.height;
288    boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow);
289    visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow);
290
291#ifndef NP_NO_QUICKDRAW
292    WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
293    ASSERT(windowRef);
294
295    // Look at the Carbon port to convert top-left-based window coordinates into top-left-based content coordinates.
296    if (isDrawingModelQuickDraw(drawingModel)) {
297        // If drawing with QuickDraw, fix the window port so that it has the same bounds as the NSWindow's
298        // content view.  This makes it easier to convert between AppKit view and QuickDraw port coordinates.
299        [self fixWindowPort];
300
301        ::Rect portBounds;
302        CGrafPtr port = GetWindowPort(windowRef);
303        GetPortBounds(port, &portBounds);
304
305        PixMap *pix = *GetPortPixMap(port);
306        boundsInWindow.origin.x += pix->bounds.left - portBounds.left;
307        boundsInWindow.origin.y += pix->bounds.top - portBounds.top;
308        visibleRectInWindow.origin.x += pix->bounds.left - portBounds.left;
309        visibleRectInWindow.origin.y += pix->bounds.top - portBounds.top;
310    }
311#endif
312
313    window.type = NPWindowTypeWindow;
314    window.x = (int32_t)boundsInWindow.origin.x;
315    window.y = (int32_t)boundsInWindow.origin.y;
316    window.width = static_cast<uint32_t>(NSWidth(boundsInWindow));
317    window.height = static_cast<uint32_t>(NSHeight(boundsInWindow));
318
319    // "Clip-out" the plug-in when:
320    // 1) it's not really in a window or off-screen or has no height or width.
321    // 2) window.x is a "big negative number" which is how WebCore expresses off-screen widgets.
322    // 3) the window is miniaturized or the app is hidden
323    // 4) we're inside of viewWillMoveToWindow: with a nil window. In this case, superviews may already have nil
324    // superviews and nil windows and results from convertRect:toView: are incorrect.
325    if (window.width <= 0 || window.height <= 0 || window.x < -100000 || [self shouldClipOutPlugin]) {
326
327        // The following code tries to give plug-ins the same size they will eventually have.
328        // The specifiedWidth and specifiedHeight variables are used to predict the size that
329        // WebCore will eventually resize us to.
330
331        // The QuickTime plug-in has problems if you give it a width or height of 0.
332        // Since other plug-ins also might have the same sort of trouble, we make sure
333        // to always give plug-ins a size other than 0,0.
334
335        if (window.width <= 0)
336            window.width = specifiedWidth > 0 ? specifiedWidth : 100;
337        if (window.height <= 0)
338            window.height = specifiedHeight > 0 ? specifiedHeight : 100;
339
340        window.clipRect.bottom = window.clipRect.top;
341        window.clipRect.left = window.clipRect.right;
342
343        // Core Animation plug-ins need to be updated (with a 0,0,0,0 clipRect) when
344        // moved to a background tab. We don't do this for Core Graphics plug-ins as
345        // older versions of Flash have historical WebKit-specific code that isn't
346        // compatible with this behavior.
347        if (drawingModel == NPDrawingModelCoreAnimation)
348            getNPRect(NSZeroRect, window.clipRect);
349    } else {
350        getNPRect(visibleRectInWindow, window.clipRect);
351    }
352
353    // Save the port state, set up the port for entry into the plugin
354    PortState portState;
355    switch (drawingModel) {
356#ifndef NP_NO_QUICKDRAW
357        case NPDrawingModelQuickDraw: {
358            // Set up NS_Port.
359            ::Rect portBounds;
360            CGrafPtr port = GetWindowPort(windowRef);
361            GetPortBounds(port, &portBounds);
362            nPort.qdPort.port = port;
363            nPort.qdPort.portx = (int32_t)-boundsInWindow.origin.x;
364            nPort.qdPort.porty = (int32_t)-boundsInWindow.origin.y;
365            window.window = &nPort;
366
367            PortState_QD *qdPortState = (PortState_QD*)malloc(sizeof(PortState_QD));
368            portState = (PortState)qdPortState;
369
370            GetGWorld(&qdPortState->oldPort, &qdPortState->oldDevice);
371
372            qdPortState->oldOrigin.h = portBounds.left;
373            qdPortState->oldOrigin.v = portBounds.top;
374
375            qdPortState->oldClipRegion = NewRgn();
376            GetPortClipRegion(port, qdPortState->oldClipRegion);
377
378            qdPortState->oldVisibleRegion = NewRgn();
379            GetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
380
381            RgnHandle clipRegion = NewRgn();
382            qdPortState->clipRegion = clipRegion;
383
384            CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
385            if (currentContext && WKCGContextIsBitmapContext(currentContext)) {
386                // We use WKCGContextIsBitmapContext here, because if we just called CGBitmapContextGetData
387                // on any context, we'd log to the console every time. But even if WKCGContextIsBitmapContext
388                // returns true, it still might not be a context we need to create a GWorld for; for example
389                // transparency layers will return true, but return 0 for CGBitmapContextGetData.
390                void* offscreenData = CGBitmapContextGetData(currentContext);
391                if (offscreenData) {
392                    // If the current context is an offscreen bitmap, then create a GWorld for it.
393                    ::Rect offscreenBounds;
394                    offscreenBounds.top = 0;
395                    offscreenBounds.left = 0;
396                    offscreenBounds.right = CGBitmapContextGetWidth(currentContext);
397                    offscreenBounds.bottom = CGBitmapContextGetHeight(currentContext);
398                    GWorldPtr newOffscreenGWorld;
399                    QDErr err = NewGWorldFromPtr(&newOffscreenGWorld,
400                        getQDPixelFormatForBitmapContext(currentContext), &offscreenBounds, 0, 0, 0,
401                        static_cast<char*>(offscreenData), CGBitmapContextGetBytesPerRow(currentContext));
402                    ASSERT(newOffscreenGWorld);
403                    ASSERT(!err);
404                    if (!err) {
405                        if (offscreenGWorld)
406                            DisposeGWorld(offscreenGWorld);
407                        offscreenGWorld = newOffscreenGWorld;
408
409                        SetGWorld(offscreenGWorld, NULL);
410
411                        port = offscreenGWorld;
412
413                        nPort.qdPort.port = port;
414                        boundsInWindow = [self bounds];
415
416                        // Generate a QD origin based on the current affine transform for currentContext.
417                        CGAffineTransform offscreenMatrix = CGContextGetCTM(currentContext);
418                        CGPoint origin = {0,0};
419                        CGPoint axisFlip = {1,1};
420                        origin = CGPointApplyAffineTransform(origin, offscreenMatrix);
421                        axisFlip = CGPointApplyAffineTransform(axisFlip, offscreenMatrix);
422
423                        // Quartz bitmaps have origins at the bottom left, but the axes may be inverted, so handle that.
424                        origin.x = offscreenBounds.left - origin.x * (axisFlip.x - origin.x);
425                        origin.y = offscreenBounds.bottom + origin.y * (axisFlip.y - origin.y);
426
427                        nPort.qdPort.portx = static_cast<int32_t>(-boundsInWindow.origin.x + origin.x);
428                        nPort.qdPort.porty = static_cast<int32_t>(-boundsInWindow.origin.y - origin.y);
429                        window.x = 0;
430                        window.y = 0;
431                        window.window = &nPort;
432
433                        // Use the clip bounds from the context instead of the bounds we created
434                        // from the window above.
435                        getNPRect(CGRectOffset(CGContextGetClipBoundingBox(currentContext), -origin.x, origin.y), window.clipRect);
436                    }
437                }
438            }
439
440            MacSetRectRgn(clipRegion,
441                window.clipRect.left + nPort.qdPort.portx, window.clipRect.top + nPort.qdPort.porty,
442                window.clipRect.right + nPort.qdPort.portx, window.clipRect.bottom + nPort.qdPort.porty);
443
444            // Clip to the dirty region if drawing to a window. When drawing to another bitmap context, do not clip.
445            if ([NSGraphicsContext currentContext] == [[self currentWindow] graphicsContext]) {
446                // Clip to dirty region so plug-in does not draw over already-drawn regions of the window that are
447                // not going to be redrawn this update.  This forces plug-ins to play nice with z-index ordering.
448                if (forUpdate) {
449                    RgnHandle viewClipRegion = NewRgn();
450
451                    // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
452                    // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
453                    // knows about the true set of dirty rects.
454                    NSView *opaqueAncestor = [self opaqueAncestor];
455                    const NSRect *dirtyRects;
456                    NSInteger dirtyRectCount, dirtyRectIndex;
457                    [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount];
458
459                    for (dirtyRectIndex = 0; dirtyRectIndex < dirtyRectCount; dirtyRectIndex++) {
460                        NSRect dirtyRect = [self convertRect:dirtyRects[dirtyRectIndex] fromView:opaqueAncestor];
461                        if (!NSEqualSizes(dirtyRect.size, NSZeroSize)) {
462                            // Create a region for this dirty rect
463                            RgnHandle dirtyRectRegion = NewRgn();
464                            SetRectRgn(dirtyRectRegion, static_cast<short>(NSMinX(dirtyRect)), static_cast<short>(NSMinY(dirtyRect)), static_cast<short>(NSMaxX(dirtyRect)), static_cast<short>(NSMaxY(dirtyRect)));
465
466                            // Union this dirty rect with the rest of the dirty rects
467                            UnionRgn(viewClipRegion, dirtyRectRegion, viewClipRegion);
468                            DisposeRgn(dirtyRectRegion);
469                        }
470                    }
471
472                    // Intersect the dirty region with the clip region, so that we only draw over dirty parts
473                    SectRgn(clipRegion, viewClipRegion, clipRegion);
474                    DisposeRgn(viewClipRegion);
475                }
476            }
477
478            // Switch to the port and set it up.
479            SetPort(port);
480            PenNormal();
481            ForeColor(blackColor);
482            BackColor(whiteColor);
483            SetOrigin(nPort.qdPort.portx, nPort.qdPort.porty);
484            SetPortClipRegion(nPort.qdPort.port, clipRegion);
485
486            if (forUpdate) {
487                // AppKit may have tried to help us by doing a BeginUpdate.
488                // But the invalid region at that level didn't include AppKit's notion of what was not valid.
489                // We reset the port's visible region to counteract what BeginUpdate did.
490                SetPortVisibleRegion(nPort.qdPort.port, clipRegion);
491                InvalWindowRgn(windowRef, clipRegion);
492            }
493
494            qdPortState->forUpdate = forUpdate;
495            break;
496        }
497#endif /* NP_NO_QUICKDRAW */
498
499        case NPDrawingModelCoreGraphics: {
500            if (![self canDraw]) {
501                portState = NULL;
502                break;
503            }
504
505            ASSERT([NSView focusView] == self);
506
507            CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
508
509            PortState_CG *cgPortState = (PortState_CG *)malloc(sizeof(PortState_CG));
510            portState = (PortState)cgPortState;
511            cgPortState->context = context;
512
513#ifndef NP_NO_CARBON
514            if (eventModel != NPEventModelCocoa) {
515                // Update the plugin's window/context
516                nPort.cgPort.window = windowRef;
517                nPort.cgPort.context = context;
518                window.window = &nPort.cgPort;
519            }
520#endif /* NP_NO_CARBON */
521
522            // Save current graphics context's state; will be restored by -restorePortState:
523            CGContextSaveGState(context);
524
525            // Clip to the dirty region if drawing to a window. When drawing to another bitmap context, do not clip.
526            if ([NSGraphicsContext currentContext] == [[self currentWindow] graphicsContext]) {
527                // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
528                // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
529                // knows about the true set of dirty rects.
530                NSView *opaqueAncestor = [self opaqueAncestor];
531                const NSRect *dirtyRects;
532                NSInteger count;
533                [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&count];
534                Vector<CGRect, 16> convertedDirtyRects;
535                convertedDirtyRects.resize(count);
536                for (int i = 0; i < count; ++i)
537                    reinterpret_cast<NSRect&>(convertedDirtyRects[i]) = [self convertRect:dirtyRects[i] fromView:opaqueAncestor];
538                CGContextClipToRects(context, convertedDirtyRects.data(), count);
539            }
540
541            break;
542        }
543
544        case NPDrawingModelCoreAnimation:
545            // Just set the port state to a dummy value.
546            portState = (PortState)1;
547            break;
548
549        default:
550            ASSERT_NOT_REACHED();
551            portState = NULL;
552            break;
553    }
554
555    return portState;
556}
557
558- (PortState)saveAndSetNewPortState
559{
560    return [self saveAndSetNewPortStateForUpdate:NO];
561}
562
563- (void)restorePortState:(PortState)portState
564{
565    ASSERT([self currentWindow]);
566    ASSERT(portState);
567
568    switch (drawingModel) {
569#ifndef NP_NO_QUICKDRAW
570        case NPDrawingModelQuickDraw: {
571            PortState_QD *qdPortState = (PortState_QD *)portState;
572            WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
573            CGrafPtr port = GetWindowPort(windowRef);
574
575            SetPort(port);
576
577            if (qdPortState->forUpdate)
578                ValidWindowRgn(windowRef, qdPortState->clipRegion);
579
580            SetOrigin(qdPortState->oldOrigin.h, qdPortState->oldOrigin.v);
581
582            SetPortClipRegion(port, qdPortState->oldClipRegion);
583            if (qdPortState->forUpdate)
584                SetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
585
586            DisposeRgn(qdPortState->oldClipRegion);
587            DisposeRgn(qdPortState->oldVisibleRegion);
588            DisposeRgn(qdPortState->clipRegion);
589
590            SetGWorld(qdPortState->oldPort, qdPortState->oldDevice);
591            break;
592        }
593#endif /* NP_NO_QUICKDRAW */
594
595        case NPDrawingModelCoreGraphics: {
596            ASSERT([NSView focusView] == self);
597
598            CGContextRef context = ((PortState_CG *)portState)->context;
599            ASSERT(!nPort.cgPort.context || (context == nPort.cgPort.context));
600            CGContextRestoreGState(context);
601            break;
602        }
603
604        case NPDrawingModelCoreAnimation:
605            ASSERT(portState == (PortState)1);
606            break;
607        default:
608            ASSERT_NOT_REACHED();
609            break;
610    }
611}
612
613- (BOOL)sendEvent:(void*)event isDrawRect:(BOOL)eventIsDrawRect
614{
615    if (![self window])
616        return NO;
617    ASSERT(event);
618
619    if (!_isStarted)
620        return NO;
621
622    ASSERT([_pluginPackage.get() pluginFuncs]->event);
623
624    // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
625    // We probably don't want more general reentrancy protection; we are really
626    // protecting only against this one case, which actually comes up when
627    // you first install the SVG viewer plug-in.
628    if (inSetWindow)
629        return NO;
630
631    Frame* frame = core([self webFrame]);
632    if (!frame)
633        return NO;
634    Page* page = frame->page();
635    if (!page)
636        return NO;
637
638    // Can only send drawRect (updateEvt) to CoreGraphics plugins when actually drawing
639    ASSERT((drawingModel != NPDrawingModelCoreGraphics) || !eventIsDrawRect || [NSView focusView] == self);
640
641    PortState portState = NULL;
642
643    if (isDrawingModelQuickDraw(drawingModel) || (drawingModel != NPDrawingModelCoreAnimation && eventIsDrawRect)) {
644        // In CoreGraphics mode, the port state only needs to be saved/set when redrawing the plug-in view.
645        // The plug-in is not allowed to draw at any other time.
646        portState = [self saveAndSetNewPortStateForUpdate:eventIsDrawRect];
647        // We may have changed the window, so inform the plug-in.
648        [self setWindowIfNecessary];
649    }
650
651#if !defined(NDEBUG) && !defined(NP_NO_QUICKDRAW)
652    // Draw green to help debug.
653    // If we see any green we know something's wrong.
654    // Note that PaintRect() only works for QuickDraw plugins; otherwise the current QD port is undefined.
655    if (isDrawingModelQuickDraw(drawingModel) && eventIsDrawRect) {
656        ForeColor(greenColor);
657        const ::Rect bigRect = { -10000, -10000, 10000, 10000 };
658        PaintRect(&bigRect);
659        ForeColor(blackColor);
660    }
661#endif
662
663    // Temporarily retain self in case the plug-in view is released while sending an event.
664    [[self retain] autorelease];
665
666    BOOL acceptedEvent;
667    [self willCallPlugInFunction];
668    // Set the pluginAllowPopup flag.
669    ASSERT(_eventHandler);
670    bool oldAllowPopups = frame->script()->allowPopupsFromPlugin();
671    frame->script()->setAllowPopupsFromPlugin(_eventHandler->currentEventIsUserGesture());
672    {
673        JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
674        acceptedEvent = [_pluginPackage.get() pluginFuncs]->event(plugin, event);
675    }
676    // Restore the old pluginAllowPopup flag.
677    frame->script()->setAllowPopupsFromPlugin(oldAllowPopups);
678    [self didCallPlugInFunction];
679
680    if (portState) {
681        if ([self currentWindow])
682            [self restorePortState:portState];
683        if (portState != (PortState)1)
684            free(portState);
685    }
686
687    return acceptedEvent;
688}
689
690- (void)windowFocusChanged:(BOOL)hasFocus
691{
692    _eventHandler->windowFocusChanged(hasFocus);
693}
694
695- (void)sendDrawRectEvent:(NSRect)rect
696{
697    ASSERT(_eventHandler);
698
699    CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
700    _eventHandler->drawRect(context, rect);
701}
702
703- (void)stopTimers
704{
705    [super stopTimers];
706
707    if (_eventHandler)
708        _eventHandler->stopTimers();
709
710    if (!timers)
711        return;
712
713    HashMap<uint32_t, PluginTimer*>::const_iterator end = timers->end();
714    for (HashMap<uint32_t, PluginTimer*>::const_iterator it = timers->begin(); it != end; ++it) {
715        PluginTimer* timer = it->second;
716        timer->stop();
717    }
718}
719
720- (void)startTimers
721{
722    [super startTimers];
723
724    // If the plugin is completely obscured (scrolled out of view, for example), then we will
725    // send null events at a reduced rate.
726    _eventHandler->startTimers(_isCompletelyObscured);
727
728    if (!timers)
729        return;
730
731    HashMap<uint32_t, PluginTimer*>::const_iterator end = timers->end();
732    for (HashMap<uint32_t, PluginTimer*>::const_iterator it = timers->begin(); it != end; ++it) {
733        PluginTimer* timer = it->second;
734        ASSERT(!timer->isActive());
735        timer->start(_isCompletelyObscured);
736    }
737}
738
739- (void)focusChanged
740{
741    // We need to null check the event handler here because
742    // the plug-in view can resign focus after it's been stopped
743    // and the event handler has been deleted.
744    if (_eventHandler)
745        _eventHandler->focusChanged(_hasFocus);
746}
747
748- (void)mouseDown:(NSEvent *)theEvent
749{
750    if (!_isStarted)
751        return;
752
753    _eventHandler->mouseDown(theEvent);
754}
755
756- (void)mouseUp:(NSEvent *)theEvent
757{
758    if (!_isStarted)
759        return;
760
761    _eventHandler->mouseUp(theEvent);
762}
763
764- (void)handleMouseEntered:(NSEvent *)theEvent
765{
766    if (!_isStarted)
767        return;
768
769    // Set cursor to arrow. Plugins often handle cursor internally, but those that don't will just get this default one.
770    [[NSCursor arrowCursor] set];
771
772    _eventHandler->mouseEntered(theEvent);
773}
774
775- (void)handleMouseExited:(NSEvent *)theEvent
776{
777    if (!_isStarted)
778        return;
779
780    _eventHandler->mouseExited(theEvent);
781
782    // 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
783    // current cursor is otherwise.  Therefore we have no choice but to unconditionally reset the cursor when the mouse exits the plugin.
784    [[NSCursor arrowCursor] set];
785}
786
787- (void)handleMouseMoved:(NSEvent *)theEvent
788{
789    if (!_isStarted)
790        return;
791
792    _eventHandler->mouseMoved(theEvent);
793}
794
795- (void)mouseDragged:(NSEvent *)theEvent
796{
797    if (!_isStarted)
798        return;
799
800    _eventHandler->mouseDragged(theEvent);
801}
802
803- (void)scrollWheel:(NSEvent *)theEvent
804{
805    if (!_isStarted) {
806        [super scrollWheel:theEvent];
807        return;
808    }
809
810    if (!_eventHandler->scrollWheel(theEvent))
811        [super scrollWheel:theEvent];
812}
813
814- (void)keyUp:(NSEvent *)theEvent
815{
816    if (!_isStarted)
817        return;
818
819    _eventHandler->keyUp(theEvent);
820}
821
822- (void)keyDown:(NSEvent *)theEvent
823{
824    if (!_isStarted)
825        return;
826
827    _eventHandler->keyDown(theEvent);
828}
829
830- (void)flagsChanged:(NSEvent *)theEvent
831{
832    if (!_isStarted)
833        return;
834
835    _eventHandler->flagsChanged(theEvent);
836}
837
838- (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character
839{
840    if (!_isStarted)
841        return;
842
843    _eventHandler->syntheticKeyDownWithCommandModifier(keyCode, character);
844}
845
846- (void)privateBrowsingModeDidChange
847{
848    if (!_isStarted)
849        return;
850
851    NPBool value = _isPrivateBrowsingEnabled;
852
853    [self willCallPlugInFunction];
854    {
855        JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
856        if ([_pluginPackage.get() pluginFuncs]->setvalue)
857            [_pluginPackage.get() pluginFuncs]->setvalue(plugin, NPNVprivateModeBool, &value);
858    }
859    [self didCallPlugInFunction];
860}
861
862// MARK: WEB_NETSCAPE_PLUGIN
863
864- (BOOL)isNewWindowEqualToOldWindow
865{
866    if (window.x != lastSetWindow.x)
867        return NO;
868    if (window.y != lastSetWindow.y)
869        return NO;
870    if (window.width != lastSetWindow.width)
871        return NO;
872    if (window.height != lastSetWindow.height)
873        return NO;
874    if (window.clipRect.top != lastSetWindow.clipRect.top)
875        return NO;
876    if (window.clipRect.left != lastSetWindow.clipRect.left)
877        return NO;
878    if (window.clipRect.bottom  != lastSetWindow.clipRect.bottom)
879        return NO;
880    if (window.clipRect.right != lastSetWindow.clipRect.right)
881        return NO;
882    if (window.type != lastSetWindow.type)
883        return NO;
884
885    switch (drawingModel) {
886#ifndef NP_NO_QUICKDRAW
887        case NPDrawingModelQuickDraw:
888            if (nPort.qdPort.portx != lastSetPort.qdPort.portx)
889                return NO;
890            if (nPort.qdPort.porty != lastSetPort.qdPort.porty)
891                return NO;
892            if (nPort.qdPort.port != lastSetPort.qdPort.port)
893                return NO;
894        break;
895#endif /* NP_NO_QUICKDRAW */
896
897        case NPDrawingModelCoreGraphics:
898            if (nPort.cgPort.window != lastSetPort.cgPort.window)
899                return NO;
900            if (nPort.cgPort.context != lastSetPort.cgPort.context)
901                return NO;
902        break;
903
904        case NPDrawingModelCoreAnimation:
905          if (window.window != lastSetWindow.window)
906              return NO;
907          break;
908        default:
909            ASSERT_NOT_REACHED();
910        break;
911    }
912
913    return YES;
914}
915
916-(void)tellQuickTimeToChill
917{
918#ifndef NP_NO_QUICKDRAW
919    ASSERT(isDrawingModelQuickDraw(drawingModel));
920
921    // Make a call to the secret QuickDraw API that makes QuickTime calm down.
922    WindowRef windowRef = (WindowRef)[[self window] windowRef];
923    if (!windowRef) {
924        return;
925    }
926    CGrafPtr port = GetWindowPort(windowRef);
927    ::Rect bounds;
928    GetPortBounds(port, &bounds);
929    WKCallDrawingNotification(port, &bounds);
930#endif /* NP_NO_QUICKDRAW */
931}
932
933- (void)updateAndSetWindow
934{
935    // A plug-in can only update if it's (1) already been started (2) isn't stopped
936    // and (3) is able to draw on-screen. To meet condition (3) the plug-in must not
937    // be hidden and be attached to a window. There are two exceptions to this rule:
938    //
939    // Exception 1: QuickDraw plug-ins must be manually told when to stop writing
940    // bits to the window backing store, thus to do so requires a new call to
941    // NPP_SetWindow() with an empty NPWindow struct.
942    //
943    // Exception 2: CoreGraphics plug-ins expect to have their drawable area updated
944    // when they are moved to a background tab, via a NPP_SetWindow call. This is
945    // accomplished by allowing -saveAndSetNewPortStateForUpdate to "clip-out" the window's
946    // clipRect. Flash is curently an exception to this. See 6453738.
947    //
948
949    if (!_isStarted)
950        return;
951
952#ifdef NP_NO_QUICKDRAW
953    if (![self canDraw])
954        return;
955#else
956    if (drawingModel == NPDrawingModelQuickDraw)
957        [self tellQuickTimeToChill];
958    else if (drawingModel == NPDrawingModelCoreGraphics && ![self canDraw] && _isFlash) {
959        // The Flash plug-in does not expect an NPP_SetWindow call from WebKit in this case.
960        // See Exception 2 above.
961        return;
962    }
963#endif // NP_NO_QUICKDRAW
964
965    BOOL didLockFocus = [NSView focusView] != self && [self lockFocusIfCanDraw];
966
967    PortState portState = [self saveAndSetNewPortState];
968    if (portState) {
969        [self setWindowIfNecessary];
970        [self restorePortState:portState];
971        if (portState != (PortState)1)
972            free(portState);
973    } else if (drawingModel == NPDrawingModelCoreGraphics)
974        [self setWindowIfNecessary];
975
976    if (didLockFocus)
977        [self unlockFocus];
978}
979
980- (void)setWindowIfNecessary
981{
982    if (!_isStarted)
983        return;
984
985    if (![self isNewWindowEqualToOldWindow]) {
986        // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
987        // We probably don't want more general reentrancy protection; we are really
988        // protecting only against this one case, which actually comes up when
989        // you first install the SVG viewer plug-in.
990        NPError npErr;
991
992        BOOL wasInSetWindow = inSetWindow;
993        inSetWindow = YES;
994        [self willCallPlugInFunction];
995        {
996            JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
997            npErr = [_pluginPackage.get() pluginFuncs]->setwindow(plugin, &window);
998        }
999        [self didCallPlugInFunction];
1000        inSetWindow = wasInSetWindow;
1001
1002#ifndef NDEBUG
1003        switch (drawingModel) {
1004#ifndef NP_NO_QUICKDRAW
1005            case NPDrawingModelQuickDraw:
1006                LOG(Plugins, "NPP_SetWindow (QuickDraw): %d, port=0x%08x, window.x:%d window.y:%d window.width:%d window.height:%d",
1007                npErr, (int)nPort.qdPort.port, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1008            break;
1009#endif /* NP_NO_QUICKDRAW */
1010
1011            case NPDrawingModelCoreGraphics:
1012                LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d window.clipRect size:%dx%d",
1013                npErr, nPort.cgPort.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height,
1014                    window.clipRect.right - window.clipRect.left, window.clipRect.bottom - window.clipRect.top);
1015            break;
1016
1017            case NPDrawingModelCoreAnimation:
1018                LOG(Plugins, "NPP_SetWindow (CoreAnimation): %d, window=%p window.x:%d window.y:%d window.width:%d window.height:%d",
1019                npErr, window.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1020            break;
1021
1022            default:
1023                ASSERT_NOT_REACHED();
1024            break;
1025        }
1026#endif /* !defined(NDEBUG) */
1027
1028        lastSetWindow = window;
1029        lastSetPort = nPort;
1030    }
1031}
1032
1033+ (void)setCurrentPluginView:(WebNetscapePluginView *)view
1034{
1035    currentPluginView = view;
1036}
1037
1038+ (WebNetscapePluginView *)currentPluginView
1039{
1040    return currentPluginView;
1041}
1042
1043- (BOOL)createPlugin
1044{
1045    // Open the plug-in package so it remains loaded while our plugin uses it
1046    [_pluginPackage.get() open];
1047
1048    // Initialize drawingModel to an invalid value so that we can detect when the plugin does not specify a drawingModel
1049    drawingModel = (NPDrawingModel)-1;
1050
1051    // Initialize eventModel to an invalid value so that we can detect when the plugin does not specify an event model.
1052    eventModel = (NPEventModel)-1;
1053
1054    NPError npErr = [self _createPlugin];
1055    if (npErr != NPERR_NO_ERROR) {
1056        LOG_ERROR("NPP_New failed with error: %d", npErr);
1057        [self _destroyPlugin];
1058        [_pluginPackage.get() close];
1059        return NO;
1060    }
1061
1062    if (drawingModel == (NPDrawingModel)-1) {
1063#ifndef NP_NO_QUICKDRAW
1064        // Default to QuickDraw if the plugin did not specify a drawing model.
1065        drawingModel = NPDrawingModelQuickDraw;
1066#else
1067        // QuickDraw is not available, so we can't default to it. Instead, default to CoreGraphics.
1068        drawingModel = NPDrawingModelCoreGraphics;
1069#endif
1070    }
1071
1072    if (eventModel == (NPEventModel)-1) {
1073        // If the plug-in did not specify a drawing model we default to Carbon when it is available.
1074#ifndef NP_NO_CARBON
1075        eventModel = NPEventModelCarbon;
1076#else
1077        eventModel = NPEventModelCocoa;
1078#endif // NP_NO_CARBON
1079    }
1080
1081#ifndef NP_NO_CARBON
1082    if (eventModel == NPEventModelCocoa && isDrawingModelQuickDraw(drawingModel)) {
1083        LOG(Plugins, "Plugin can't use use Cocoa event model with QuickDraw drawing model: %@", _pluginPackage.get());
1084        [self _destroyPlugin];
1085        [_pluginPackage.get() close];
1086
1087        return NO;
1088    }
1089#endif // NP_NO_CARBON
1090
1091#ifndef BUILDING_ON_TIGER
1092    if (drawingModel == NPDrawingModelCoreAnimation) {
1093        void *value = 0;
1094        if ([_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginCoreAnimationLayer, &value) == NPERR_NO_ERROR && value) {
1095
1096            // The plug-in gives us a retained layer.
1097            _pluginLayer.adoptNS((CALayer *)value);
1098
1099            BOOL accleratedCompositingEnabled = false;
1100#if USE(ACCELERATED_COMPOSITING)
1101            accleratedCompositingEnabled = [[[self webView] preferences] acceleratedCompositingEnabled];
1102#endif
1103            if (accleratedCompositingEnabled) {
1104                // FIXME: This code can be shared between WebHostedNetscapePluginView and WebNetscapePluginView.
1105#ifndef BUILDING_ON_LEOPARD
1106                // Since this layer isn't going to be inserted into a view, we need to create another layer and flip its geometry
1107                // in order to get the coordinate system right.
1108                RetainPtr<CALayer> realPluginLayer(AdoptNS, _pluginLayer.releaseRef());
1109
1110                _pluginLayer.adoptNS([[CALayer alloc] init]);
1111                _pluginLayer.get().bounds = realPluginLayer.get().bounds;
1112                _pluginLayer.get().geometryFlipped = YES;
1113
1114                realPluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
1115                [_pluginLayer.get() addSublayer:realPluginLayer.get()];
1116#endif
1117                // Eagerly enter compositing mode, since we know we'll need it. This avoids firing setNeedsStyleRecalc()
1118                // for iframes that contain composited plugins at bad times. https://bugs.webkit.org/show_bug.cgi?id=39033
1119                core([self webFrame])->view()->enterCompositingMode();
1120                [self element]->setNeedsStyleRecalc(SyntheticStyleChange);
1121            } else
1122                [self setWantsLayer:YES];
1123
1124            LOG(Plugins, "%@ is using Core Animation drawing model with layer %@", _pluginPackage.get(), _pluginLayer.get());
1125        }
1126
1127        ASSERT(_pluginLayer);
1128    }
1129#endif
1130
1131    // Create the event handler
1132    _eventHandler.set(WebNetscapePluginEventHandler::create(self));
1133
1134    return YES;
1135}
1136
1137#ifndef BUILDING_ON_TIGER
1138// FIXME: This method is an ideal candidate to move up to the base class
1139- (CALayer *)pluginLayer
1140{
1141    return _pluginLayer.get();
1142}
1143
1144- (void)setLayer:(CALayer *)newLayer
1145{
1146    [super setLayer:newLayer];
1147
1148    if (newLayer && _pluginLayer) {
1149        _pluginLayer.get().frame = [newLayer frame];
1150        _pluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
1151        [newLayer addSublayer:_pluginLayer.get()];
1152    }
1153}
1154#endif
1155
1156- (void)loadStream
1157{
1158    if ([self _shouldCancelSrcStream])
1159        return;
1160
1161    if (_loadManually) {
1162        [self _redeliverStream];
1163        return;
1164    }
1165
1166    // If the OBJECT/EMBED tag has no SRC, the URL is passed to us as "".
1167    // Check for this and don't start a load in this case.
1168    if (_sourceURL && ![_sourceURL.get() _web_isEmpty]) {
1169        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_sourceURL.get()];
1170        [request _web_setHTTPReferrer:core([self webFrame])->loader()->outgoingReferrer()];
1171        [self loadRequest:request inTarget:nil withNotifyData:nil sendNotification:NO];
1172    }
1173}
1174
1175- (BOOL)shouldStop
1176{
1177    // If we're already calling a plug-in function, do not call NPP_Destroy().  The plug-in function we are calling
1178    // may assume that its instance->pdata, or other memory freed by NPP_Destroy(), is valid and unchanged until said
1179    // plugin-function returns.
1180    // See <rdar://problem/4480737>.
1181    if (pluginFunctionCallDepth > 0) {
1182        shouldStopSoon = YES;
1183        return NO;
1184    }
1185
1186    return YES;
1187}
1188
1189- (void)destroyPlugin
1190{
1191    // To stop active streams it's necessary to invoke stop() on a copy
1192    // of streams. This is because calling WebNetscapePluginStream::stop() also has the side effect
1193    // of removing a stream from this hash set.
1194    Vector<RefPtr<WebNetscapePluginStream> > streamsCopy;
1195    copyToVector(streams, streamsCopy);
1196    for (size_t i = 0; i < streamsCopy.size(); i++)
1197        streamsCopy[i]->stop();
1198
1199    [[_pendingFrameLoads.get() allKeys] makeObjectsPerformSelector:@selector(_setInternalLoadDelegate:) withObject:nil];
1200    [NSObject cancelPreviousPerformRequestsWithTarget:self];
1201
1202    // Setting the window type to 0 ensures that NPP_SetWindow will be called if the plug-in is restarted.
1203    lastSetWindow.type = (NPWindowType)0;
1204
1205#ifndef BUILDING_ON_TIGER
1206    _pluginLayer = 0;
1207#endif
1208
1209    [self _destroyPlugin];
1210    [_pluginPackage.get() close];
1211
1212    _eventHandler.clear();
1213}
1214
1215- (NPEventModel)eventModel
1216{
1217    return eventModel;
1218}
1219
1220- (NPP)plugin
1221{
1222    return plugin;
1223}
1224
1225- (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values
1226{
1227    ASSERT([keys count] == [values count]);
1228
1229    // Convert the attributes to 2 C string arrays.
1230    // These arrays are passed to NPP_New, but the strings need to be
1231    // modifiable and live the entire life of the plugin.
1232
1233    // The Java plug-in requires the first argument to be the base URL
1234    if ([_MIMEType.get() isEqualToString:@"application/x-java-applet"]) {
1235        cAttributes = (char **)malloc(([keys count] + 1) * sizeof(char *));
1236        cValues = (char **)malloc(([values count] + 1) * sizeof(char *));
1237        cAttributes[0] = strdup("DOCBASE");
1238        cValues[0] = strdup([_baseURL.get() _web_URLCString]);
1239        argsCount++;
1240    } else {
1241        cAttributes = (char **)malloc([keys count] * sizeof(char *));
1242        cValues = (char **)malloc([values count] * sizeof(char *));
1243    }
1244
1245    BOOL isWMP = [_pluginPackage.get() bundleIdentifier] == "com.microsoft.WMP.defaultplugin";
1246
1247    unsigned i;
1248    unsigned count = [keys count];
1249    for (i = 0; i < count; i++) {
1250        NSString *key = [keys objectAtIndex:i];
1251        NSString *value = [values objectAtIndex:i];
1252        if ([key _webkit_isCaseInsensitiveEqualToString:@"height"]) {
1253            specifiedHeight = [value intValue];
1254        } else if ([key _webkit_isCaseInsensitiveEqualToString:@"width"]) {
1255            specifiedWidth = [value intValue];
1256        }
1257        // Avoid Window Media Player crash when these attributes are present.
1258        if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) {
1259            continue;
1260        }
1261        cAttributes[argsCount] = strdup([key UTF8String]);
1262        cValues[argsCount] = strdup([value UTF8String]);
1263        LOG(Plugins, "%@ = %@", key, value);
1264        argsCount++;
1265    }
1266}
1267
1268- (uint32_t)checkIfAllowedToLoadURL:(const char*)urlCString frame:(const char*)frameNameCString
1269                       callbackFunc:(void (*)(NPP npp, uint32_t checkID, NPBool allowed, void* context))callbackFunc
1270                            context:(void*)context
1271{
1272    if (!_containerChecksInProgress)
1273        _containerChecksInProgress = [[NSMutableDictionary alloc] init];
1274
1275    NSString *frameName = frameNameCString ? [NSString stringWithCString:frameNameCString encoding:NSISOLatin1StringEncoding] : nil;
1276
1277    ++_currentContainerCheckRequestID;
1278    WebNetscapeContainerCheckContextInfo *contextInfo = [[WebNetscapeContainerCheckContextInfo alloc] initWithCheckRequestID:_currentContainerCheckRequestID
1279                                                                                                                callbackFunc:callbackFunc
1280                                                                                                                      context:context];
1281
1282    WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:[self requestWithURLCString:urlCString]
1283                                                                        target:frameName
1284                                                                  resultObject:self
1285                                                                      selector:@selector(_containerCheckResult:contextInfo:)
1286                                                                    controller:self
1287                                                                   contextInfo:contextInfo];
1288
1289    [contextInfo release];
1290    [_containerChecksInProgress setObject:check forKey:[NSNumber numberWithInt:_currentContainerCheckRequestID]];
1291    [check start];
1292
1293    return _currentContainerCheckRequestID;
1294}
1295
1296- (void)_containerCheckResult:(PolicyAction)policy contextInfo:(id)contextInfo
1297{
1298    ASSERT([contextInfo isKindOfClass:[WebNetscapeContainerCheckContextInfo class]]);
1299    void (*pluginCallback)(NPP npp, uint32_t, NPBool, void*) = [contextInfo callback];
1300
1301    if (!pluginCallback) {
1302        ASSERT_NOT_REACHED();
1303        return;
1304    }
1305
1306    pluginCallback([self plugin], [contextInfo checkRequestID], (policy == PolicyUse), [contextInfo context]);
1307}
1308
1309- (void)cancelCheckIfAllowedToLoadURL:(uint32_t)checkID
1310{
1311    WebPluginContainerCheck *check = (WebPluginContainerCheck *)[_containerChecksInProgress objectForKey:[NSNumber numberWithInt:checkID]];
1312
1313    if (!check)
1314        return;
1315
1316    [check cancel];
1317    [_containerChecksInProgress removeObjectForKey:[NSNumber numberWithInt:checkID]];
1318}
1319
1320// WebPluginContainerCheck automatically calls this method after invoking our _containerCheckResult: selector.
1321// It works this way because calling -[WebPluginContainerCheck cancel] allows it to do it's teardown process.
1322- (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)webPluginContainerCheck
1323{
1324    ASSERT([webPluginContainerCheck isKindOfClass:[WebPluginContainerCheck class]]);
1325    WebPluginContainerCheck *check = (WebPluginContainerCheck *)webPluginContainerCheck;
1326    ASSERT([[check contextInfo] isKindOfClass:[WebNetscapeContainerCheckContextInfo class]]);
1327
1328    [self cancelCheckIfAllowedToLoadURL:[[check contextInfo] checkRequestID]];
1329}
1330
1331#ifdef BUILDING_ON_TIGER
1332// The Tiger compiler requires these two methods be present. Otherwise it doesn't think WebNetscapePluginView
1333// conforms to the WebPluginContainerCheckController protocol.
1334- (WebView *)webView
1335{
1336    return [super webView];
1337}
1338
1339- (WebFrame *)webFrame
1340{
1341    return [super webFrame];
1342}
1343#endif
1344
1345// MARK: NSVIEW
1346
1347- (id)initWithFrame:(NSRect)frame
1348      pluginPackage:(WebNetscapePluginPackage *)pluginPackage
1349                URL:(NSURL *)URL
1350            baseURL:(NSURL *)baseURL
1351           MIMEType:(NSString *)MIME
1352      attributeKeys:(NSArray *)keys
1353    attributeValues:(NSArray *)values
1354       loadManually:(BOOL)loadManually
1355            element:(PassRefPtr<WebCore::HTMLPlugInElement>)element
1356{
1357    self = [super initWithFrame:frame pluginPackage:pluginPackage URL:URL baseURL:baseURL MIMEType:MIME attributeKeys:keys attributeValues:values loadManually:loadManually element:element];
1358    if (!self)
1359        return nil;
1360
1361    _pendingFrameLoads.adoptNS([[NSMutableDictionary alloc] init]);
1362
1363    // load the plug-in if it is not already loaded
1364    if (![pluginPackage load]) {
1365        [self release];
1366        return nil;
1367    }
1368
1369    return self;
1370}
1371
1372- (id)initWithFrame:(NSRect)frame
1373{
1374    ASSERT_NOT_REACHED();
1375    return nil;
1376}
1377
1378- (void)fini
1379{
1380#ifndef NP_NO_QUICKDRAW
1381    if (offscreenGWorld)
1382        DisposeGWorld(offscreenGWorld);
1383#endif
1384
1385    for (unsigned i = 0; i < argsCount; i++) {
1386        free(cAttributes[i]);
1387        free(cValues[i]);
1388    }
1389    free(cAttributes);
1390    free(cValues);
1391
1392    ASSERT(!_eventHandler);
1393
1394    if (timers) {
1395        deleteAllValues(*timers);
1396        delete timers;
1397    }
1398
1399    [_containerChecksInProgress release];
1400}
1401
1402- (void)disconnectStream:(WebNetscapePluginStream*)stream
1403{
1404    streams.remove(stream);
1405}
1406
1407- (void)dealloc
1408{
1409    ASSERT(!_isStarted);
1410    ASSERT(!plugin);
1411
1412    [self fini];
1413
1414    [super dealloc];
1415}
1416
1417- (void)finalize
1418{
1419    ASSERT_MAIN_THREAD();
1420    ASSERT(!_isStarted);
1421
1422    [self fini];
1423
1424    [super finalize];
1425}
1426
1427- (void)drawRect:(NSRect)rect
1428{
1429    if (_cachedSnapshot) {
1430        NSRect sourceRect = { NSZeroPoint, [_cachedSnapshot.get() size] };
1431        [_cachedSnapshot.get() drawInRect:[self bounds] fromRect:sourceRect operation:NSCompositeSourceOver fraction:1];
1432        return;
1433    }
1434
1435    if (drawingModel == NPDrawingModelCoreAnimation && (!_snapshotting || ![self supportsSnapshotting]))
1436        return;
1437
1438    if (!_isStarted)
1439        return;
1440
1441    if ([NSGraphicsContext currentContextDrawingToScreen] || _isFlash)
1442        [self sendDrawRectEvent:rect];
1443    else {
1444        NSBitmapImageRep *printedPluginBitmap = [self _printedPluginBitmap];
1445        if (printedPluginBitmap) {
1446            // Flip the bitmap before drawing because the QuickDraw port is flipped relative
1447            // to this view.
1448            CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
1449            CGContextSaveGState(cgContext);
1450            NSRect bounds = [self bounds];
1451            CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
1452            CGContextScaleCTM(cgContext, 1.0f, -1.0f);
1453            [printedPluginBitmap drawInRect:bounds];
1454            CGContextRestoreGState(cgContext);
1455        }
1456    }
1457}
1458
1459- (NPObject *)createPluginScriptableObject
1460{
1461    if (![_pluginPackage.get() pluginFuncs]->getvalue || !_isStarted)
1462        return NULL;
1463
1464    NPObject *value = NULL;
1465    NPError error;
1466    [self willCallPlugInFunction];
1467    {
1468        JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
1469        error = [_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginScriptableNPObject, &value);
1470    }
1471    [self didCallPlugInFunction];
1472    if (error != NPERR_NO_ERROR)
1473        return NULL;
1474
1475    return value;
1476}
1477
1478- (void)willCallPlugInFunction
1479{
1480    ASSERT(plugin);
1481
1482    // Could try to prevent infinite recursion here, but it's probably not worth the effort.
1483    pluginFunctionCallDepth++;
1484}
1485
1486- (void)didCallPlugInFunction
1487{
1488    ASSERT(pluginFunctionCallDepth > 0);
1489    pluginFunctionCallDepth--;
1490
1491    // If -stop was called while we were calling into a plug-in function, and we're no longer
1492    // inside a plug-in function, stop now.
1493    if (pluginFunctionCallDepth == 0 && shouldStopSoon) {
1494        shouldStopSoon = NO;
1495        [self stop];
1496    }
1497}
1498
1499-(void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
1500{
1501    ASSERT(_loadManually);
1502    ASSERT(!_manualStream);
1503
1504    _manualStream = WebNetscapePluginStream::create(core([self webFrame])->loader());
1505}
1506
1507- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
1508{
1509    ASSERT(_loadManually);
1510    ASSERT(_manualStream);
1511
1512    _dataLengthReceived += [data length];
1513
1514    if (!_isStarted)
1515        return;
1516
1517    if (!_manualStream->plugin()) {
1518        // Check if the load should be cancelled
1519        if ([self _shouldCancelSrcStream]) {
1520            NSURLResponse *response = [[self dataSource] response];
1521
1522            NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad
1523                                                            contentURL:[response URL]
1524                                                         pluginPageURL:nil
1525                                                            pluginName:nil // FIXME: Get this from somewhere
1526                                                              MIMEType:[response MIMEType]];
1527            [[self dataSource] _documentLoader]->cancelMainResourceLoad(error);
1528            [error release];
1529            return;
1530        }
1531
1532        _manualStream->setRequestURL([[[self dataSource] request] URL]);
1533        _manualStream->setPlugin([self plugin]);
1534        ASSERT(_manualStream->plugin());
1535
1536        _manualStream->startStreamWithResponse([[self dataSource] response]);
1537    }
1538
1539    if (_manualStream->plugin())
1540        _manualStream->didReceiveData(0, static_cast<const char *>([data bytes]), [data length]);
1541}
1542
1543- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
1544{
1545    ASSERT(_loadManually);
1546
1547    _error = error;
1548
1549    if (!_isStarted) {
1550        return;
1551    }
1552
1553    _manualStream->destroyStreamWithError(error);
1554}
1555
1556- (void)pluginViewFinishedLoading:(NSView *)pluginView
1557{
1558    ASSERT(_loadManually);
1559    ASSERT(_manualStream);
1560
1561    if (_isStarted)
1562        _manualStream->didFinishLoading(0);
1563}
1564
1565- (NSTextInputContext *)inputContext
1566{
1567    return nil;
1568}
1569
1570@end
1571
1572@implementation WebNetscapePluginView (WebNPPCallbacks)
1573
1574- (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest
1575{
1576    // FIXME: Is this isStarted check needed here? evaluateJavaScriptPluginRequest should not be called
1577    // if we are stopped since this method is called after a delay and we call
1578    // cancelPreviousPerformRequestsWithTarget inside of stop.
1579    if (!_isStarted) {
1580        return;
1581    }
1582
1583    NSURL *URL = [[JSPluginRequest request] URL];
1584    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1585    ASSERT(JSString);
1586
1587    NSString *result = [[self webFrame] _stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]];
1588
1589    // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop.
1590    if (!_isStarted) {
1591        return;
1592    }
1593
1594    if ([JSPluginRequest frameName] != nil) {
1595        // FIXME: If the result is a string, we probably want to put that string into the frame.
1596        if ([JSPluginRequest sendNotification]) {
1597            [self willCallPlugInFunction];
1598            {
1599                JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
1600                [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]);
1601            }
1602            [self didCallPlugInFunction];
1603        }
1604    } else if ([result length] > 0) {
1605        // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does.
1606        NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding];
1607
1608        RefPtr<WebNetscapePluginStream> stream = WebNetscapePluginStream::create([NSURLRequest requestWithURL:URL], plugin, [JSPluginRequest sendNotification], [JSPluginRequest notifyData]);
1609
1610        RetainPtr<NSURLResponse> response(AdoptNS, [[NSURLResponse alloc] initWithURL:URL
1611                                                                             MIMEType:@"text/plain"
1612                                                                expectedContentLength:[JSData length]
1613                                                                     textEncodingName:nil]);
1614
1615        stream->startStreamWithResponse(response.get());
1616        stream->didReceiveData(0, static_cast<const char*>([JSData bytes]), [JSData length]);
1617        stream->didFinishLoading(0);
1618    }
1619}
1620
1621- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
1622{
1623    ASSERT(_isStarted);
1624
1625    WebPluginRequest *pluginRequest = [_pendingFrameLoads.get() objectForKey:webFrame];
1626    ASSERT(pluginRequest != nil);
1627    ASSERT([pluginRequest sendNotification]);
1628
1629    [self willCallPlugInFunction];
1630    {
1631        JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
1632        [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]);
1633    }
1634    [self didCallPlugInFunction];
1635
1636    [_pendingFrameLoads.get() removeObjectForKey:webFrame];
1637    [webFrame _setInternalLoadDelegate:nil];
1638}
1639
1640- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
1641{
1642    NPReason reason = NPRES_DONE;
1643    if (error != nil)
1644        reason = WebNetscapePluginStream::reasonForError(error);
1645    [self webFrame:webFrame didFinishLoadWithReason:reason];
1646}
1647
1648- (void)loadPluginRequest:(WebPluginRequest *)pluginRequest
1649{
1650    NSURLRequest *request = [pluginRequest request];
1651    NSString *frameName = [pluginRequest frameName];
1652    WebFrame *frame = nil;
1653
1654    NSURL *URL = [request URL];
1655    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1656
1657    ASSERT(frameName || JSString);
1658
1659    if (frameName) {
1660        // FIXME - need to get rid of this window creation which
1661        // bypasses normal targeted link handling
1662        frame = kit(core([self webFrame])->loader()->findFrameForNavigation(frameName));
1663        if (frame == nil) {
1664            WebView *currentWebView = [self webView];
1665            NSDictionary *features = [[NSDictionary alloc] init];
1666            WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView
1667                                                        createWebViewWithRequest:nil
1668                                                                  windowFeatures:features];
1669            [features release];
1670
1671            if (!newWebView) {
1672                if ([pluginRequest sendNotification]) {
1673                    [self willCallPlugInFunction];
1674                    {
1675                        JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
1676                        [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [[[pluginRequest request] URL] _web_URLCString], NPERR_GENERIC_ERROR, [pluginRequest notifyData]);
1677                    }
1678                    [self didCallPlugInFunction];
1679                }
1680                return;
1681            }
1682
1683            frame = [newWebView mainFrame];
1684            core(frame)->tree()->setName(frameName);
1685            [[newWebView _UIDelegateForwarder] webViewShow:newWebView];
1686        }
1687    }
1688
1689    if (JSString) {
1690        ASSERT(frame == nil || [self webFrame] == frame);
1691        [self evaluateJavaScriptPluginRequest:pluginRequest];
1692    } else {
1693        [frame loadRequest:request];
1694        if ([pluginRequest sendNotification]) {
1695            // Check if another plug-in view or even this view is waiting for the frame to load.
1696            // If it is, tell it that the load was cancelled because it will be anyway.
1697            WebNetscapePluginView *view = [frame _internalLoadDelegate];
1698            if (view != nil) {
1699                ASSERT([view isKindOfClass:[WebNetscapePluginView class]]);
1700                [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK];
1701            }
1702            [_pendingFrameLoads.get() _webkit_setObject:pluginRequest forUncopiedKey:frame];
1703            [frame _setInternalLoadDelegate:self];
1704        }
1705    }
1706}
1707
1708- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification
1709{
1710    NSURL *URL = [request URL];
1711
1712    if (!URL)
1713        return NPERR_INVALID_URL;
1714
1715    // Don't allow requests to be loaded when the document loader is stopping all loaders.
1716    if ([[self dataSource] _documentLoader]->isStopping())
1717        return NPERR_GENERIC_ERROR;
1718
1719    NSString *target = nil;
1720    if (cTarget) {
1721        // Find the frame given the target string.
1722        target = [NSString stringWithCString:cTarget encoding:NSISOLatin1StringEncoding];
1723    }
1724    WebFrame *frame = [self webFrame];
1725
1726    // don't let a plugin start any loads if it is no longer part of a document that is being
1727    // displayed unless the loads are in the same frame as the plugin.
1728    if ([[self dataSource] _documentLoader] != core([self webFrame])->loader()->activeDocumentLoader() &&
1729        (!cTarget || [frame findFrameNamed:target] != frame)) {
1730        return NPERR_GENERIC_ERROR;
1731    }
1732
1733    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1734    if (JSString != nil) {
1735        if (![[[self webView] preferences] isJavaScriptEnabled]) {
1736            // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does.
1737            return NPERR_GENERIC_ERROR;
1738        } else if (cTarget == NULL && _mode == NP_FULL) {
1739            // Don't allow a JavaScript request from a standalone plug-in that is self-targetted
1740            // because this can cause the user to be redirected to a blank page (3424039).
1741            return NPERR_INVALID_PARAM;
1742        }
1743    } else {
1744        if (!core([self webFrame])->document()->securityOrigin()->canDisplay(URL))
1745            return NPERR_GENERIC_ERROR;
1746    }
1747
1748    if (cTarget || JSString) {
1749        // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't
1750        // want to potentially kill the plug-in inside of its URL request.
1751
1752        if (JSString && target && [frame findFrameNamed:target] != frame) {
1753            // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
1754            return NPERR_INVALID_PARAM;
1755        }
1756
1757        bool currentEventIsUserGesture = false;
1758        if (_eventHandler)
1759            currentEventIsUserGesture = _eventHandler->currentEventIsUserGesture();
1760
1761        WebPluginRequest *pluginRequest = [[WebPluginRequest alloc] initWithRequest:request
1762                                                                          frameName:target
1763                                                                         notifyData:notifyData
1764                                                                   sendNotification:sendNotification
1765                                                            didStartFromUserGesture:currentEventIsUserGesture];
1766        [self performSelector:@selector(loadPluginRequest:) withObject:pluginRequest afterDelay:0];
1767        [pluginRequest release];
1768    } else {
1769        RefPtr<WebNetscapePluginStream> stream = WebNetscapePluginStream::create(request, plugin, sendNotification, notifyData);
1770
1771        streams.add(stream.get());
1772        stream->start();
1773    }
1774
1775    return NPERR_NO_ERROR;
1776}
1777
1778-(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData
1779{
1780    LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget);
1781
1782    NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1783    return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES];
1784}
1785
1786-(NPError)getURL:(const char *)URLCString target:(const char *)cTarget
1787{
1788    LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget);
1789
1790    NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1791    return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO];
1792}
1793
1794- (NPError)_postURL:(const char *)URLCString
1795             target:(const char *)target
1796                len:(UInt32)len
1797                buf:(const char *)buf
1798               file:(NPBool)file
1799         notifyData:(void *)notifyData
1800   sendNotification:(BOOL)sendNotification
1801       allowHeaders:(BOOL)allowHeaders
1802{
1803    if (!URLCString || !len || !buf) {
1804        return NPERR_INVALID_PARAM;
1805    }
1806
1807    NSData *postData = nil;
1808
1809    if (file) {
1810        // If we're posting a file, buf is either a file URL or a path to the file.
1811        NSString *bufString = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingWindowsLatin1);
1812        if (!bufString) {
1813            return NPERR_INVALID_PARAM;
1814        }
1815        NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString];
1816        NSString *path;
1817        if ([fileURL isFileURL]) {
1818            path = [fileURL path];
1819        } else {
1820            path = bufString;
1821        }
1822        postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]];
1823        CFRelease(bufString);
1824        if (!postData) {
1825            return NPERR_FILE_NOT_FOUND;
1826        }
1827    } else {
1828        postData = [NSData dataWithBytes:buf length:len];
1829    }
1830
1831    if ([postData length] == 0) {
1832        return NPERR_INVALID_PARAM;
1833    }
1834
1835    NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1836    [request setHTTPMethod:@"POST"];
1837
1838    if (allowHeaders) {
1839        if ([postData _web_startsWithBlankLine]) {
1840            postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)];
1841        } else {
1842            NSInteger location = [postData _web_locationAfterFirstBlankLine];
1843            if (location != NSNotFound) {
1844                // If the blank line is somewhere in the middle of postData, everything before is the header.
1845                NSData *headerData = [postData subdataWithRange:NSMakeRange(0, location)];
1846                NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields];
1847                unsigned dataLength = [postData length] - location;
1848
1849                // Sometimes plugins like to set Content-Length themselves when they post,
1850                // but WebFoundation does not like that. So we will remove the header
1851                // and instead truncate the data to the requested length.
1852                NSString *contentLength = [header objectForKey:@"Content-Length"];
1853
1854                if (contentLength != nil)
1855                    dataLength = min<unsigned>([contentLength intValue], dataLength);
1856                [header removeObjectForKey:@"Content-Length"];
1857
1858                if ([header count] > 0) {
1859                    [request setAllHTTPHeaderFields:header];
1860                }
1861                // Everything after the blank line is the actual content of the POST.
1862                postData = [postData subdataWithRange:NSMakeRange(location, dataLength)];
1863
1864            }
1865        }
1866        if ([postData length] == 0) {
1867            return NPERR_INVALID_PARAM;
1868        }
1869    }
1870
1871    // Plug-ins expect to receive uncached data when doing a POST (3347134).
1872    [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
1873    [request setHTTPBody:postData];
1874
1875    return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification];
1876}
1877
1878- (NPError)postURLNotify:(const char *)URLCString
1879                  target:(const char *)target
1880                     len:(UInt32)len
1881                     buf:(const char *)buf
1882                    file:(NPBool)file
1883              notifyData:(void *)notifyData
1884{
1885    LOG(Plugins, "NPN_PostURLNotify: %s", URLCString);
1886    return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:notifyData sendNotification:YES allowHeaders:YES];
1887}
1888
1889-(NPError)postURL:(const char *)URLCString
1890           target:(const char *)target
1891              len:(UInt32)len
1892              buf:(const char *)buf
1893             file:(NPBool)file
1894{
1895    LOG(Plugins, "NPN_PostURL: %s", URLCString);
1896    // As documented, only allow headers to be specified via NPP_PostURL when using a file.
1897    return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:NULL sendNotification:NO allowHeaders:file];
1898}
1899
1900-(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream
1901{
1902    LOG(Plugins, "NPN_NewStream");
1903    return NPERR_GENERIC_ERROR;
1904}
1905
1906-(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer
1907{
1908    LOG(Plugins, "NPN_Write");
1909    return NPERR_GENERIC_ERROR;
1910}
1911
1912-(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason
1913{
1914    LOG(Plugins, "NPN_DestroyStream");
1915    // This function does a sanity check to ensure that the NPStream provided actually
1916    // belongs to the plug-in that provided it, which fixes a crash in the DivX
1917    // plug-in: <rdar://problem/5093862> | http://bugs.webkit.org/show_bug.cgi?id=13203
1918    if (!stream || WebNetscapePluginStream::ownerForStream(stream) != plugin) {
1919        LOG(Plugins, "Invalid NPStream passed to NPN_DestroyStream: %p", stream);
1920        return NPERR_INVALID_INSTANCE_ERROR;
1921    }
1922
1923    WebNetscapePluginStream* browserStream = static_cast<WebNetscapePluginStream*>(stream->ndata);
1924    browserStream->cancelLoadAndDestroyStreamWithError(browserStream->errorForReason(reason));
1925
1926    return NPERR_NO_ERROR;
1927}
1928
1929- (const char *)userAgent
1930{
1931    NSString *userAgent = [[self webView] userAgentForURL:_baseURL.get()];
1932
1933    if (_isSilverlight) {
1934        // Silverlight has a workaround for a leak in Safari 2. This workaround is
1935        // applied when the user agent does not contain "Version/3" so we append it
1936        // at the end of the user agent.
1937        userAgent = [userAgent stringByAppendingString:@" Version/3.2.1"];
1938    }
1939
1940    return [userAgent UTF8String];
1941}
1942
1943-(void)status:(const char *)message
1944{
1945    CFStringRef status = CFStringCreateWithCString(NULL, message ? message : "", kCFStringEncodingUTF8);
1946    if (!status) {
1947        LOG_ERROR("NPN_Status: the message was not valid UTF-8");
1948        return;
1949    }
1950
1951    LOG(Plugins, "NPN_Status: %@", status);
1952    WebView *wv = [self webView];
1953    [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status];
1954    CFRelease(status);
1955}
1956
1957-(void)invalidateRect:(NPRect *)invalidRect
1958{
1959    LOG(Plugins, "NPN_InvalidateRect");
1960    [self invalidatePluginContentRect:NSMakeRect(invalidRect->left, invalidRect->top,
1961        (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)];
1962}
1963
1964- (void)invalidateRegion:(NPRegion)invalidRegion
1965{
1966    LOG(Plugins, "NPN_InvalidateRegion");
1967    NSRect invalidRect = NSZeroRect;
1968    switch (drawingModel) {
1969#ifndef NP_NO_QUICKDRAW
1970        case NPDrawingModelQuickDraw:
1971        {
1972            ::Rect qdRect;
1973            GetRegionBounds((NPQDRegion)invalidRegion, &qdRect);
1974            invalidRect = NSMakeRect(qdRect.left, qdRect.top, qdRect.right - qdRect.left, qdRect.bottom - qdRect.top);
1975        }
1976        break;
1977#endif /* NP_NO_QUICKDRAW */
1978
1979        case NPDrawingModelCoreGraphics:
1980        {
1981            CGRect cgRect = CGPathGetBoundingBox((NPCGRegion)invalidRegion);
1982            invalidRect = *(NSRect*)&cgRect;
1983            break;
1984        }
1985        default:
1986            ASSERT_NOT_REACHED();
1987        break;
1988    }
1989
1990    [self invalidatePluginContentRect:invalidRect];
1991}
1992
1993-(void)forceRedraw
1994{
1995    LOG(Plugins, "forceRedraw");
1996    [self invalidatePluginContentRect:[self bounds]];
1997    [[self window] displayIfNeeded];
1998}
1999
2000- (NPError)getVariable:(NPNVariable)variable value:(void *)value
2001{
2002    switch (variable) {
2003        case NPNVWindowNPObject:
2004        {
2005            Frame* frame = core([self webFrame]);
2006            NPObject* windowScriptObject = frame ? frame->script()->windowScriptNPObject() : 0;
2007
2008            // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
2009            if (windowScriptObject)
2010                _NPN_RetainObject(windowScriptObject);
2011
2012            void **v = (void **)value;
2013            *v = windowScriptObject;
2014
2015            return NPERR_NO_ERROR;
2016        }
2017
2018        case NPNVPluginElementNPObject:
2019        {
2020            if (!_element)
2021                return NPERR_GENERIC_ERROR;
2022
2023            NPObject *plugInScriptObject = _element->getNPObject();
2024
2025            // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
2026            if (plugInScriptObject)
2027                _NPN_RetainObject(plugInScriptObject);
2028
2029            void **v = (void **)value;
2030            *v = plugInScriptObject;
2031
2032            return NPERR_NO_ERROR;
2033        }
2034
2035        case NPNVpluginDrawingModel:
2036        {
2037            *(NPDrawingModel *)value = drawingModel;
2038            return NPERR_NO_ERROR;
2039        }
2040
2041#ifndef NP_NO_QUICKDRAW
2042        case NPNVsupportsQuickDrawBool:
2043        {
2044            *(NPBool *)value = TRUE;
2045            return NPERR_NO_ERROR;
2046        }
2047#endif /* NP_NO_QUICKDRAW */
2048
2049        case NPNVsupportsCoreGraphicsBool:
2050        {
2051            *(NPBool *)value = TRUE;
2052            return NPERR_NO_ERROR;
2053        }
2054
2055        case NPNVsupportsOpenGLBool:
2056        {
2057            *(NPBool *)value = FALSE;
2058            return NPERR_NO_ERROR;
2059        }
2060
2061        case NPNVsupportsCoreAnimationBool:
2062        {
2063#ifdef BUILDING_ON_TIGER
2064            *(NPBool *)value = FALSE;
2065#else
2066            *(NPBool *)value = TRUE;
2067#endif
2068            return NPERR_NO_ERROR;
2069        }
2070
2071#ifndef NP_NO_CARBON
2072        case NPNVsupportsCarbonBool:
2073        {
2074            *(NPBool *)value = TRUE;
2075            return NPERR_NO_ERROR;
2076        }
2077#endif /* NP_NO_CARBON */
2078
2079        case NPNVsupportsCocoaBool:
2080        {
2081            *(NPBool *)value = TRUE;
2082            return NPERR_NO_ERROR;
2083        }
2084
2085        case NPNVprivateModeBool:
2086        {
2087            *(NPBool *)value = _isPrivateBrowsingEnabled;
2088            return NPERR_NO_ERROR;
2089        }
2090
2091        case WKNVBrowserContainerCheckFuncs:
2092        {
2093            *(WKNBrowserContainerCheckFuncs **)value = browserContainerCheckFuncs();
2094            return NPERR_NO_ERROR;
2095        }
2096#if USE(ACCELERATED_COMPOSITING)
2097        case WKNVSupportsCompositingCoreAnimationPluginsBool:
2098        {
2099            *(NPBool *)value = [[[self webView] preferences] acceleratedCompositingEnabled];
2100            return NPERR_NO_ERROR;
2101        }
2102#endif
2103        default:
2104            break;
2105    }
2106
2107    return NPERR_GENERIC_ERROR;
2108}
2109
2110- (NPError)setVariable:(NPPVariable)variable value:(void *)value
2111{
2112    switch (variable) {
2113        case NPPVpluginDrawingModel:
2114        {
2115            // Can only set drawing model inside NPP_New()
2116            if (self != [[self class] currentPluginView])
2117                return NPERR_GENERIC_ERROR;
2118
2119            // Check for valid, supported drawing model
2120            NPDrawingModel newDrawingModel = (NPDrawingModel)(uintptr_t)value;
2121            switch (newDrawingModel) {
2122                // Supported drawing models:
2123#ifndef NP_NO_QUICKDRAW
2124                case NPDrawingModelQuickDraw:
2125#endif
2126                case NPDrawingModelCoreGraphics:
2127#ifndef BUILDING_ON_TIGER
2128                case NPDrawingModelCoreAnimation:
2129#endif
2130                    drawingModel = newDrawingModel;
2131                    return NPERR_NO_ERROR;
2132
2133
2134                // Unsupported (or unknown) drawing models:
2135                default:
2136                    LOG(Plugins, "Plugin %@ uses unsupported drawing model: %d", _eventHandler.get(), drawingModel);
2137                    return NPERR_GENERIC_ERROR;
2138            }
2139        }
2140
2141        case NPPVpluginEventModel:
2142        {
2143            // Can only set event model inside NPP_New()
2144            if (self != [[self class] currentPluginView])
2145                return NPERR_GENERIC_ERROR;
2146
2147            // Check for valid, supported event model
2148            NPEventModel newEventModel = (NPEventModel)(uintptr_t)value;
2149            switch (newEventModel) {
2150                // Supported event models:
2151#ifndef NP_NO_CARBON
2152                case NPEventModelCarbon:
2153#endif
2154                case NPEventModelCocoa:
2155                    eventModel = newEventModel;
2156                    return NPERR_NO_ERROR;
2157
2158                    // Unsupported (or unknown) event models:
2159                default:
2160                    LOG(Plugins, "Plugin %@ uses unsupported event model: %d", _eventHandler.get(), eventModel);
2161                    return NPERR_GENERIC_ERROR;
2162            }
2163        }
2164
2165        default:
2166            return NPERR_GENERIC_ERROR;
2167    }
2168}
2169
2170- (uint32_t)scheduleTimerWithInterval:(uint32_t)interval repeat:(NPBool)repeat timerFunc:(void (*)(NPP npp, uint32_t timerID))timerFunc
2171{
2172    if (!timerFunc)
2173        return 0;
2174
2175    if (!timers)
2176        timers = new HashMap<uint32_t, PluginTimer*>;
2177
2178    uint32_t timerID;
2179
2180    do {
2181        timerID = ++currentTimerID;
2182    } while (timers->contains(timerID) || timerID == 0);
2183
2184    PluginTimer* timer = new PluginTimer(plugin, timerID, interval, repeat, timerFunc);
2185    timers->set(timerID, timer);
2186
2187    if (_shouldFireTimers)
2188        timer->start(_isCompletelyObscured);
2189
2190    return timerID;
2191}
2192
2193- (void)unscheduleTimer:(uint32_t)timerID
2194{
2195    if (!timers)
2196        return;
2197
2198    if (PluginTimer* timer = timers->take(timerID))
2199        delete timer;
2200}
2201
2202- (NPError)popUpContextMenu:(NPMenu *)menu
2203{
2204    NSEvent *currentEvent = [NSApp currentEvent];
2205
2206    // NPN_PopUpContextMenu must be called from within the plug-in's NPP_HandleEvent.
2207    if (!currentEvent)
2208        return NPERR_GENERIC_ERROR;
2209
2210    [NSMenu popUpContextMenu:(NSMenu *)menu withEvent:currentEvent forView:self];
2211    return NPERR_NO_ERROR;
2212}
2213
2214- (NPError)getVariable:(NPNURLVariable)variable forURL:(const char*)url value:(char**)value length:(uint32_t*)length
2215{
2216    switch (variable) {
2217        case NPNURLVCookie: {
2218            if (!value)
2219                break;
2220
2221            NSURL *URL = [self URLWithCString:url];
2222            if (!URL)
2223                break;
2224
2225            if (Frame* frame = core([self webFrame])) {
2226                String cookieString = cookies(frame->document(), URL);
2227                CString cookieStringUTF8 = cookieString.utf8();
2228                if (cookieStringUTF8.isNull())
2229                    return NPERR_GENERIC_ERROR;
2230
2231                *value = static_cast<char*>(NPN_MemAlloc(cookieStringUTF8.length()));
2232                memcpy(*value, cookieStringUTF8.data(), cookieStringUTF8.length());
2233
2234                if (length)
2235                    *length = cookieStringUTF8.length();
2236                return NPERR_NO_ERROR;
2237            }
2238            break;
2239        }
2240        case NPNURLVProxy: {
2241#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
2242            if (!value)
2243                break;
2244
2245            NSURL *URL = [self URLWithCString:url];
2246            if (!URL)
2247                break;
2248
2249            Vector<ProxyServer> proxyServers = proxyServersForURL(URL, 0);
2250            CString proxiesUTF8 = toString(proxyServers).utf8();
2251
2252            *value = static_cast<char*>(NPN_MemAlloc(proxiesUTF8.length()));
2253            memcpy(*value, proxiesUTF8.data(), proxiesUTF8.length());
2254
2255           if (length)
2256               *length = proxiesUTF8.length();
2257
2258            return NPERR_NO_ERROR;
2259#else
2260            break;
2261#endif
2262        }
2263    }
2264    return NPERR_GENERIC_ERROR;
2265}
2266
2267- (NPError)setVariable:(NPNURLVariable)variable forURL:(const char*)url value:(const char*)value length:(uint32_t)length
2268{
2269    switch (variable) {
2270        case NPNURLVCookie: {
2271            NSURL *URL = [self URLWithCString:url];
2272            if (!URL)
2273                break;
2274
2275            String cookieString = String::fromUTF8(value, length);
2276            if (!cookieString)
2277                break;
2278
2279            if (Frame* frame = core([self webFrame])) {
2280                setCookies(frame->document(), URL, cookieString);
2281                return NPERR_NO_ERROR;
2282            }
2283
2284            break;
2285        }
2286        case NPNURLVProxy:
2287            // Can't set the proxy for a URL.
2288            break;
2289    }
2290    return NPERR_GENERIC_ERROR;
2291}
2292
2293- (NPError)getAuthenticationInfoWithProtocol:(const char*)protocolStr host:(const char*)hostStr port:(int32_t)port scheme:(const char*)schemeStr realm:(const char*)realmStr
2294                                    username:(char**)usernameStr usernameLength:(uint32_t*)usernameLength
2295                                    password:(char**)passwordStr passwordLength:(uint32_t*)passwordLength
2296{
2297    if (!protocolStr || !hostStr || !schemeStr || !realmStr || !usernameStr || !usernameLength || !passwordStr || !passwordLength)
2298        return NPERR_GENERIC_ERROR;
2299
2300    CString username;
2301    CString password;
2302    if (!getAuthenticationInfo(protocolStr, hostStr, port, schemeStr, realmStr, username, password))
2303        return NPERR_GENERIC_ERROR;
2304
2305    *usernameLength = username.length();
2306    *usernameStr = static_cast<char*>(NPN_MemAlloc(username.length()));
2307    memcpy(*usernameStr, username.data(), username.length());
2308
2309    *passwordLength = password.length();
2310    *passwordStr = static_cast<char*>(NPN_MemAlloc(password.length()));
2311    memcpy(*passwordStr, password.data(), password.length());
2312
2313    return NPERR_NO_ERROR;
2314}
2315
2316- (char*)resolveURL:(const char*)url forTarget:(const char*)target
2317{
2318    CString location = [self resolvedURLStringForURL:url target:target];
2319
2320    if (location.isNull())
2321        return 0;
2322
2323    // We use strdup here because the caller needs to free it with NPN_MemFree (which calls free).
2324    return strdup(location.data());
2325}
2326
2327@end
2328
2329@implementation WebNetscapePluginView (Internal)
2330
2331- (BOOL)_shouldCancelSrcStream
2332{
2333    ASSERT(_isStarted);
2334
2335    // Check if we should cancel the load
2336    NPBool cancelSrcStream = 0;
2337    if ([_pluginPackage.get() pluginFuncs]->getvalue &&
2338        [_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginCancelSrcStream, &cancelSrcStream) == NPERR_NO_ERROR && cancelSrcStream)
2339        return YES;
2340
2341    return NO;
2342}
2343
2344// Work around Silverlight full screen performance issue by maintaining an accelerated GL pixel format.
2345// We can safely remove it at some point in the future when both:
2346// 1) Microsoft releases a genuine fix for 7288546.
2347// 2) Enough Silverlight users update to the new Silverlight.
2348// For now, we'll distinguish older broken versions of Silverlight by asking the plug-in if it resolved its full screen badness.
2349- (void)_workaroundSilverlightFullscreenBug:(BOOL)initializedPlugin
2350{
2351#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
2352    ASSERT(_isSilverlight);
2353    NPBool isFullscreenPerformanceIssueFixed = 0;
2354    NPPluginFuncs *pluginFuncs = [_pluginPackage.get() pluginFuncs];
2355    if (pluginFuncs->getvalue && pluginFuncs->getvalue(plugin, static_cast<NPPVariable>(WKNVSilverlightFullscreenPerformanceIssueFixed), &isFullscreenPerformanceIssueFixed) == NPERR_NO_ERROR && isFullscreenPerformanceIssueFixed)
2356        return;
2357
2358    static CGLPixelFormatObj pixelFormatObject = 0;
2359    static unsigned refCount = 0;
2360
2361    if (initializedPlugin) {
2362        refCount++;
2363        if (refCount == 1) {
2364            const CGLPixelFormatAttribute attributes[] = { kCGLPFAAccelerated, static_cast<CGLPixelFormatAttribute>(0) };
2365            GLint npix;
2366            CGLChoosePixelFormat(attributes, &pixelFormatObject, &npix);
2367        }
2368    } else {
2369        ASSERT(pixelFormatObject);
2370        refCount--;
2371        if (!refCount)
2372            CGLReleasePixelFormat(pixelFormatObject);
2373    }
2374#endif
2375}
2376
2377- (NPError)_createPlugin
2378{
2379    plugin = (NPP)calloc(1, sizeof(NPP_t));
2380    plugin->ndata = self;
2381
2382    ASSERT([_pluginPackage.get() pluginFuncs]->newp);
2383
2384    // NPN_New(), which creates the plug-in instance, should never be called while calling a plug-in function for that instance.
2385    ASSERT(pluginFunctionCallDepth == 0);
2386
2387    PluginMainThreadScheduler::scheduler().registerPlugin(plugin);
2388
2389    _isFlash = [_pluginPackage.get() bundleIdentifier] == "com.macromedia.Flash Player.plugin";
2390    _isSilverlight = [_pluginPackage.get() bundleIdentifier] == "com.microsoft.SilverlightPlugin";
2391
2392    [[self class] setCurrentPluginView:self];
2393    NPError npErr = [_pluginPackage.get() pluginFuncs]->newp((char *)[_MIMEType.get() cString], plugin, _mode, argsCount, cAttributes, cValues, NULL);
2394    [[self class] setCurrentPluginView:nil];
2395    if (_isSilverlight)
2396        [self _workaroundSilverlightFullscreenBug:YES];
2397    LOG(Plugins, "NPP_New: %d", npErr);
2398    return npErr;
2399}
2400
2401- (void)_destroyPlugin
2402{
2403    PluginMainThreadScheduler::scheduler().unregisterPlugin(plugin);
2404
2405    if (_isSilverlight)
2406        [self _workaroundSilverlightFullscreenBug:NO];
2407
2408    NPError npErr;
2409    npErr = ![_pluginPackage.get() pluginFuncs]->destroy(plugin, NULL);
2410    LOG(Plugins, "NPP_Destroy: %d", npErr);
2411
2412    if (Frame* frame = core([self webFrame]))
2413        frame->script()->cleanupScriptObjectsForPlugin(self);
2414
2415    free(plugin);
2416    plugin = NULL;
2417}
2418
2419- (NSBitmapImageRep *)_printedPluginBitmap
2420{
2421#ifdef NP_NO_QUICKDRAW
2422    return nil;
2423#else
2424    // Cannot print plugins that do not implement NPP_Print
2425    if (![_pluginPackage.get() pluginFuncs]->print)
2426        return nil;
2427
2428    // This NSBitmapImageRep will share its bitmap buffer with a GWorld that the plugin will draw into.
2429    // The bitmap is created in 32-bits-per-pixel ARGB format, which is the default GWorld pixel format.
2430    NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
2431                                                         pixelsWide:window.width
2432                                                         pixelsHigh:window.height
2433                                                         bitsPerSample:8
2434                                                         samplesPerPixel:4
2435                                                         hasAlpha:YES
2436                                                         isPlanar:NO
2437                                                         colorSpaceName:NSDeviceRGBColorSpace
2438                                                         bitmapFormat:NSAlphaFirstBitmapFormat
2439                                                         bytesPerRow:0
2440                                                         bitsPerPixel:0] autorelease];
2441    ASSERT(bitmap);
2442
2443    // Create a GWorld with the same underlying buffer into which the plugin can draw
2444    ::Rect printGWorldBounds;
2445    SetRect(&printGWorldBounds, 0, 0, window.width, window.height);
2446    GWorldPtr printGWorld;
2447    if (NewGWorldFromPtr(&printGWorld,
2448                         k32ARGBPixelFormat,
2449                         &printGWorldBounds,
2450                         NULL,
2451                         NULL,
2452                         0,
2453                         (Ptr)[bitmap bitmapData],
2454                         [bitmap bytesPerRow]) != noErr) {
2455        LOG_ERROR("Could not create GWorld for printing");
2456        return nil;
2457    }
2458
2459    /// Create NPWindow for the GWorld
2460    NPWindow printNPWindow;
2461    printNPWindow.window = &printGWorld; // Normally this is an NP_Port, but when printing it is the actual CGrafPtr
2462    printNPWindow.x = 0;
2463    printNPWindow.y = 0;
2464    printNPWindow.width = window.width;
2465    printNPWindow.height = window.height;
2466    printNPWindow.clipRect.top = 0;
2467    printNPWindow.clipRect.left = 0;
2468    printNPWindow.clipRect.right = window.width;
2469    printNPWindow.clipRect.bottom = window.height;
2470    printNPWindow.type = NPWindowTypeDrawable; // Offscreen graphics port as opposed to a proper window
2471
2472    // Create embed-mode NPPrint
2473    NPPrint npPrint;
2474    npPrint.mode = NP_EMBED;
2475    npPrint.print.embedPrint.window = printNPWindow;
2476    npPrint.print.embedPrint.platformPrint = printGWorld;
2477
2478    // Tell the plugin to print into the GWorld
2479    [self willCallPlugInFunction];
2480    {
2481        JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
2482        [_pluginPackage.get() pluginFuncs]->print(plugin, &npPrint);
2483    }
2484    [self didCallPlugInFunction];
2485
2486    // Don't need the GWorld anymore
2487    DisposeGWorld(printGWorld);
2488
2489    return bitmap;
2490#endif
2491}
2492
2493- (void)_redeliverStream
2494{
2495    if ([self dataSource] && _isStarted) {
2496        // Deliver what has not been passed to the plug-in up to this point.
2497        if (_dataLengthReceived > 0) {
2498            NSData *data = [[[self dataSource] data] subdataWithRange:NSMakeRange(0, _dataLengthReceived)];
2499            _dataLengthReceived = 0;
2500            [self pluginView:self receivedData:data];
2501            if (![[self dataSource] isLoading]) {
2502                if (_error)
2503                    [self pluginView:self receivedError:_error.get()];
2504                else
2505                    [self pluginViewFinishedLoading:self];
2506            }
2507        }
2508    }
2509}
2510
2511@end
2512
2513#endif
2514