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 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/UserGestureIndicator.h>
74#import <WebCore/WebCoreObjCExtras.h>
75#import <WebCore/WebCoreURLResponse.h>
76#import <WebCore/npruntime_impl.h>
77#import <WebKitLegacy/DOMPrivate.h>
78#import <WebKitLegacy/WebUIDelegate.h>
79#import <objc/runtime.h>
80#import <runtime/InitializeThreading.h>
81#import <runtime/JSLock.h>
82#import <wtf/Assertions.h>
83#import <wtf/MainThread.h>
84#import <wtf/RunLoop.h>
85#import <wtf/text/CString.h>
86
87#define LoginWindowDidSwitchFromUserNotification    @"WebLoginWindowDidSwitchFromUserNotification"
88#define LoginWindowDidSwitchToUserNotification      @"WebLoginWindowDidSwitchToUserNotification"
89#define WKNVSupportsCompositingCoreAnimationPluginsBool 74656  /* TRUE if the browser supports hardware compositing of Core Animation plug-ins  */
90static const int WKNVSilverlightFullscreenPerformanceIssueFixed = 7288546; /* TRUE if Siverlight addressed its underlying  bug in <rdar://problem/7288546> */
91
92using namespace WebCore;
93using namespace WebKit;
94
95static inline bool isDrawingModelQuickDraw(NPDrawingModel drawingModel)
96{
97#ifndef NP_NO_QUICKDRAW
98    return drawingModel == NPDrawingModelQuickDraw;
99#else
100    return false;
101#endif
102};
103
104@interface WebNetscapePluginView (Internal)
105- (NPError)_createPlugin;
106- (void)_destroyPlugin;
107- (NSBitmapImageRep *)_printedPluginBitmap;
108- (void)_redeliverStream;
109- (BOOL)_shouldCancelSrcStream;
110@end
111
112static WebNetscapePluginView *currentPluginView = nil;
113
114typedef struct OpaquePortState* PortState;
115
116static const double ThrottledTimerInterval = 0.25;
117
118class PluginTimer : public TimerBase {
119public:
120    typedef void (*TimerFunc)(NPP npp, uint32_t timerID);
121
122    PluginTimer(NPP npp, uint32_t timerID, uint32_t interval, NPBool repeat, TimerFunc timerFunc)
123        : m_npp(npp)
124        , m_timerID(timerID)
125        , m_interval(interval)
126        , m_repeat(repeat)
127        , m_timerFunc(timerFunc)
128    {
129    }
130
131    void start(bool throttle)
132    {
133        ASSERT(!isActive());
134
135        double timeInterval = m_interval / 1000.0;
136
137        if (throttle)
138            timeInterval = std::max(timeInterval, ThrottledTimerInterval);
139
140        if (m_repeat)
141            startRepeating(timeInterval);
142        else
143            startOneShot(timeInterval);
144    }
145
146private:
147    virtual void fired()
148    {
149        m_timerFunc(m_npp, m_timerID);
150        if (!m_repeat)
151            delete this;
152    }
153
154    NPP m_npp;
155    uint32_t m_timerID;
156    uint32_t m_interval;
157    NPBool m_repeat;
158    TimerFunc m_timerFunc;
159};
160
161#ifndef NP_NO_QUICKDRAW
162
163// QuickDraw is not available in 64-bit
164
165typedef struct {
166    GrafPtr oldPort;
167    GDHandle oldDevice;
168    Point oldOrigin;
169    RgnHandle oldClipRegion;
170    RgnHandle oldVisibleRegion;
171    RgnHandle clipRegion;
172    BOOL forUpdate;
173} PortState_QD;
174
175#endif /* NP_NO_QUICKDRAW */
176
177typedef struct {
178    CGContextRef context;
179} PortState_CG;
180
181@class NSTextInputContext;
182@interface NSResponder (AppKitDetails)
183- (NSTextInputContext *)inputContext;
184@end
185
186@interface WebNetscapePluginView (ForwardDeclarations)
187- (void)setWindowIfNecessary;
188- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification;
189@end
190
191@implementation WebNetscapePluginView
192
193+ (void)initialize
194{
195    JSC::initializeThreading();
196    WTF::initializeMainThreadToProcessMainThread();
197    RunLoop::initializeMainRunLoop();
198    WebCoreObjCFinalizeOnMainThread(self);
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    {
671        JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
672        UserGestureIndicator gestureIndicator(_eventHandler->currentEventIsUserGesture() ? DefinitelyProcessingUserGesture : PossiblyProcessingUserGesture);
673        acceptedEvent = [_pluginPackage.get() pluginFuncs]->event(plugin, event);
674    }
675    [self didCallPlugInFunction];
676
677    if (portState) {
678        if ([self currentWindow])
679            [self restorePortState:portState];
680        if (portState != (PortState)1)
681            free(portState);
682    }
683
684    return acceptedEvent;
685}
686
687- (void)windowFocusChanged:(BOOL)hasFocus
688{
689    _eventHandler->windowFocusChanged(hasFocus);
690}
691
692- (void)sendDrawRectEvent:(NSRect)rect
693{
694    ASSERT(_eventHandler);
695
696    CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
697    _eventHandler->drawRect(context, rect);
698}
699
700- (void)stopTimers
701{
702    [super stopTimers];
703
704    if (_eventHandler)
705        _eventHandler->stopTimers();
706
707    if (!timers)
708        return;
709
710    for (auto& it: timers->values())
711        it->stop();
712}
713
714- (void)startTimers
715{
716    [super startTimers];
717
718    // If the plugin is completely obscured (scrolled out of view, for example), then we will
719    // send null events at a reduced rate.
720    _eventHandler->startTimers(_isCompletelyObscured);
721
722    if (!timers)
723        return;
724
725    for (auto& it: timers->values()) {
726        ASSERT(!it->isActive());
727        it->start(_isCompletelyObscured);
728    }
729}
730
731- (void)focusChanged
732{
733    // We need to null check the event handler here because
734    // the plug-in view can resign focus after it's been stopped
735    // and the event handler has been deleted.
736    if (_eventHandler)
737        _eventHandler->focusChanged(_hasFocus);
738}
739
740- (void)mouseDown:(NSEvent *)theEvent
741{
742    if (!_isStarted)
743        return;
744
745    _eventHandler->mouseDown(theEvent);
746}
747
748- (void)mouseUp:(NSEvent *)theEvent
749{
750    if (!_isStarted)
751        return;
752
753    _eventHandler->mouseUp(theEvent);
754}
755
756- (void)handleMouseEntered:(NSEvent *)theEvent
757{
758    if (!_isStarted)
759        return;
760
761    // Set cursor to arrow. Plugins often handle cursor internally, but those that don't will just get this default one.
762    [[NSCursor arrowCursor] set];
763
764    _eventHandler->mouseEntered(theEvent);
765}
766
767- (void)handleMouseExited:(NSEvent *)theEvent
768{
769    if (!_isStarted)
770        return;
771
772    _eventHandler->mouseExited(theEvent);
773
774    // 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
775    // current cursor is otherwise.  Therefore we have no choice but to unconditionally reset the cursor when the mouse exits the plugin.
776    [[NSCursor arrowCursor] set];
777}
778
779- (void)handleMouseMoved:(NSEvent *)theEvent
780{
781    if (!_isStarted)
782        return;
783
784    _eventHandler->mouseMoved(theEvent);
785}
786
787- (void)mouseDragged:(NSEvent *)theEvent
788{
789    if (!_isStarted)
790        return;
791
792    _eventHandler->mouseDragged(theEvent);
793}
794
795- (void)scrollWheel:(NSEvent *)theEvent
796{
797    if (!_isStarted) {
798        [super scrollWheel:theEvent];
799        return;
800    }
801
802    if (!_eventHandler->scrollWheel(theEvent))
803        [super scrollWheel:theEvent];
804}
805
806- (void)keyUp:(NSEvent *)theEvent
807{
808    if (!_isStarted)
809        return;
810
811    _eventHandler->keyUp(theEvent);
812}
813
814- (void)keyDown:(NSEvent *)theEvent
815{
816    if (!_isStarted)
817        return;
818
819    _eventHandler->keyDown(theEvent);
820}
821
822- (void)flagsChanged:(NSEvent *)theEvent
823{
824    if (!_isStarted)
825        return;
826
827    _eventHandler->flagsChanged(theEvent);
828}
829
830- (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character
831{
832    if (!_isStarted)
833        return;
834
835    _eventHandler->syntheticKeyDownWithCommandModifier(keyCode, character);
836}
837
838- (void)privateBrowsingModeDidChange
839{
840    if (!_isStarted)
841        return;
842
843    NPBool value = _isPrivateBrowsingEnabled;
844
845    [self willCallPlugInFunction];
846    {
847        JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
848        if ([_pluginPackage.get() pluginFuncs]->setvalue)
849            [_pluginPackage.get() pluginFuncs]->setvalue(plugin, NPNVprivateModeBool, &value);
850    }
851    [self didCallPlugInFunction];
852}
853
854// MARK: WEB_NETSCAPE_PLUGIN
855
856- (BOOL)isNewWindowEqualToOldWindow
857{
858    if (window.x != lastSetWindow.x)
859        return NO;
860    if (window.y != lastSetWindow.y)
861        return NO;
862    if (window.width != lastSetWindow.width)
863        return NO;
864    if (window.height != lastSetWindow.height)
865        return NO;
866    if (window.clipRect.top != lastSetWindow.clipRect.top)
867        return NO;
868    if (window.clipRect.left != lastSetWindow.clipRect.left)
869        return NO;
870    if (window.clipRect.bottom  != lastSetWindow.clipRect.bottom)
871        return NO;
872    if (window.clipRect.right != lastSetWindow.clipRect.right)
873        return NO;
874    if (window.type != lastSetWindow.type)
875        return NO;
876
877    switch (drawingModel) {
878#ifndef NP_NO_QUICKDRAW
879        case NPDrawingModelQuickDraw:
880            if (nPort.qdPort.portx != lastSetPort.qdPort.portx)
881                return NO;
882            if (nPort.qdPort.porty != lastSetPort.qdPort.porty)
883                return NO;
884            if (nPort.qdPort.port != lastSetPort.qdPort.port)
885                return NO;
886        break;
887#endif /* NP_NO_QUICKDRAW */
888
889        case NPDrawingModelCoreGraphics:
890            if (nPort.cgPort.window != lastSetPort.cgPort.window)
891                return NO;
892            if (nPort.cgPort.context != lastSetPort.cgPort.context)
893                return NO;
894        break;
895
896        case NPDrawingModelCoreAnimation:
897          if (window.window != lastSetWindow.window)
898              return NO;
899          break;
900        default:
901            ASSERT_NOT_REACHED();
902        break;
903    }
904
905    return YES;
906}
907
908-(void)tellQuickTimeToChill
909{
910#ifndef NP_NO_QUICKDRAW
911    ASSERT(isDrawingModelQuickDraw(drawingModel));
912
913    // Make a call to the secret QuickDraw API that makes QuickTime calm down.
914    WindowRef windowRef = (WindowRef)[[self window] windowRef];
915    if (!windowRef) {
916        return;
917    }
918    CGrafPtr port = GetWindowPort(windowRef);
919    ::Rect bounds;
920    GetPortBounds(port, &bounds);
921    WKCallDrawingNotification(port, &bounds);
922#endif /* NP_NO_QUICKDRAW */
923}
924
925- (void)updateAndSetWindow
926{
927    // A plug-in can only update if it's (1) already been started (2) isn't stopped
928    // and (3) is able to draw on-screen. To meet condition (3) the plug-in must not
929    // be hidden and be attached to a window. There are two exceptions to this rule:
930    //
931    // Exception 1: QuickDraw plug-ins must be manually told when to stop writing
932    // bits to the window backing store, thus to do so requires a new call to
933    // NPP_SetWindow() with an empty NPWindow struct.
934    //
935    // Exception 2: CoreGraphics plug-ins expect to have their drawable area updated
936    // when they are moved to a background tab, via a NPP_SetWindow call. This is
937    // accomplished by allowing -saveAndSetNewPortStateForUpdate to "clip-out" the window's
938    // clipRect. Flash is curently an exception to this. See 6453738.
939    //
940
941    if (!_isStarted)
942        return;
943
944#ifdef NP_NO_QUICKDRAW
945    if (![self canDraw])
946        return;
947#else
948    if (drawingModel == NPDrawingModelQuickDraw)
949        [self tellQuickTimeToChill];
950    else if (drawingModel == NPDrawingModelCoreGraphics && ![self canDraw] && _isFlash) {
951        // The Flash plug-in does not expect an NPP_SetWindow call from WebKit in this case.
952        // See Exception 2 above.
953        return;
954    }
955#endif // NP_NO_QUICKDRAW
956
957    BOOL didLockFocus = [NSView focusView] != self && [self lockFocusIfCanDraw];
958
959    PortState portState = [self saveAndSetNewPortState];
960    if (portState) {
961        [self setWindowIfNecessary];
962        [self restorePortState:portState];
963        if (portState != (PortState)1)
964            free(portState);
965    } else if (drawingModel == NPDrawingModelCoreGraphics)
966        [self setWindowIfNecessary];
967
968    if (didLockFocus)
969        [self unlockFocus];
970}
971
972- (void)setWindowIfNecessary
973{
974    if (!_isStarted)
975        return;
976
977    if (![self isNewWindowEqualToOldWindow]) {
978        // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
979        // We probably don't want more general reentrancy protection; we are really
980        // protecting only against this one case, which actually comes up when
981        // you first install the SVG viewer plug-in.
982        NPError npErr;
983
984        BOOL wasInSetWindow = inSetWindow;
985        inSetWindow = YES;
986        [self willCallPlugInFunction];
987        {
988            JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
989            npErr = [_pluginPackage.get() pluginFuncs]->setwindow(plugin, &window);
990        }
991        [self didCallPlugInFunction];
992        inSetWindow = wasInSetWindow;
993
994#ifndef NDEBUG
995        switch (drawingModel) {
996#ifndef NP_NO_QUICKDRAW
997            case NPDrawingModelQuickDraw:
998                LOG(Plugins, "NPP_SetWindow (QuickDraw): %d, port=0x%08x, window.x:%d window.y:%d window.width:%d window.height:%d",
999                npErr, (int)nPort.qdPort.port, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1000            break;
1001#endif /* NP_NO_QUICKDRAW */
1002
1003            case NPDrawingModelCoreGraphics:
1004                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",
1005                npErr, nPort.cgPort.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height,
1006                    window.clipRect.right - window.clipRect.left, window.clipRect.bottom - window.clipRect.top);
1007            break;
1008
1009            case NPDrawingModelCoreAnimation:
1010                LOG(Plugins, "NPP_SetWindow (CoreAnimation): %d, window=%p window.x:%d window.y:%d window.width:%d window.height:%d",
1011                npErr, window.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
1012            break;
1013
1014            default:
1015                ASSERT_NOT_REACHED();
1016            break;
1017        }
1018#endif /* !defined(NDEBUG) */
1019
1020        lastSetWindow = window;
1021        lastSetPort = nPort;
1022    }
1023}
1024
1025+ (void)setCurrentPluginView:(WebNetscapePluginView *)view
1026{
1027    currentPluginView = view;
1028}
1029
1030+ (WebNetscapePluginView *)currentPluginView
1031{
1032    return currentPluginView;
1033}
1034
1035- (BOOL)createPlugin
1036{
1037    // Open the plug-in package so it remains loaded while our plugin uses it
1038    [_pluginPackage.get() open];
1039
1040    // Initialize drawingModel to an invalid value so that we can detect when the plugin does not specify a drawingModel
1041    drawingModel = (NPDrawingModel)-1;
1042
1043    // Initialize eventModel to an invalid value so that we can detect when the plugin does not specify an event model.
1044    eventModel = (NPEventModel)-1;
1045
1046    NPError npErr = [self _createPlugin];
1047    if (npErr != NPERR_NO_ERROR) {
1048        LOG_ERROR("NPP_New failed with error: %d", npErr);
1049        [self _destroyPlugin];
1050        [_pluginPackage.get() close];
1051        return NO;
1052    }
1053
1054    if (drawingModel == (NPDrawingModel)-1) {
1055#ifndef NP_NO_QUICKDRAW
1056        // Default to QuickDraw if the plugin did not specify a drawing model.
1057        drawingModel = NPDrawingModelQuickDraw;
1058#else
1059        // QuickDraw is not available, so we can't default to it. Instead, default to CoreGraphics.
1060        drawingModel = NPDrawingModelCoreGraphics;
1061#endif
1062    }
1063
1064    if (eventModel == (NPEventModel)-1) {
1065        // If the plug-in did not specify a drawing model we default to Carbon when it is available.
1066#ifndef NP_NO_CARBON
1067        eventModel = NPEventModelCarbon;
1068#else
1069        eventModel = NPEventModelCocoa;
1070#endif // NP_NO_CARBON
1071    }
1072
1073#ifndef NP_NO_CARBON
1074    if (eventModel == NPEventModelCocoa && isDrawingModelQuickDraw(drawingModel)) {
1075        LOG(Plugins, "Plugin can't use use Cocoa event model with QuickDraw drawing model: %@", _pluginPackage.get());
1076        [self _destroyPlugin];
1077        [_pluginPackage.get() close];
1078
1079        return NO;
1080    }
1081#endif // NP_NO_CARBON
1082
1083    if (drawingModel == NPDrawingModelCoreAnimation) {
1084        void *value = 0;
1085        if ([_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginCoreAnimationLayer, &value) == NPERR_NO_ERROR && value) {
1086
1087            // The plug-in gives us a retained layer.
1088            _pluginLayer = adoptNS((CALayer *)value);
1089
1090            BOOL accleratedCompositingEnabled = false;
1091            accleratedCompositingEnabled = [[[self webView] preferences] acceleratedCompositingEnabled];
1092            if (accleratedCompositingEnabled) {
1093                // FIXME: This code can be shared between WebHostedNetscapePluginView and WebNetscapePluginView.
1094                // Since this layer isn't going to be inserted into a view, we need to create another layer and flip its geometry
1095                // in order to get the coordinate system right.
1096                RetainPtr<CALayer> realPluginLayer = adoptNS(_pluginLayer.leakRef());
1097
1098                _pluginLayer = adoptNS([[CALayer alloc] init]);
1099                _pluginLayer.get().bounds = realPluginLayer.get().bounds;
1100                _pluginLayer.get().geometryFlipped = YES;
1101
1102                realPluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
1103                [_pluginLayer.get() addSublayer:realPluginLayer.get()];
1104
1105                // Eagerly enter compositing mode, since we know we'll need it. This avoids firing setNeedsStyleRecalc()
1106                // for iframes that contain composited plugins at bad times. https://bugs.webkit.org/show_bug.cgi?id=39033
1107                core([self webFrame])->view()->enterCompositingMode();
1108                [self element]->setNeedsStyleRecalc(SyntheticStyleChange);
1109            } else
1110                [self setWantsLayer:YES];
1111
1112            LOG(Plugins, "%@ is using Core Animation drawing model with layer %@", _pluginPackage.get(), _pluginLayer.get());
1113        }
1114
1115        ASSERT(_pluginLayer);
1116    }
1117
1118    // Create the event handler
1119    _eventHandler = WebNetscapePluginEventHandler::create(self);
1120
1121    return YES;
1122}
1123
1124// FIXME: This method is an ideal candidate to move up to the base class
1125- (CALayer *)pluginLayer
1126{
1127    return _pluginLayer.get();
1128}
1129
1130- (void)setLayer:(CALayer *)newLayer
1131{
1132    [super setLayer:newLayer];
1133
1134    if (newLayer && _pluginLayer) {
1135        _pluginLayer.get().frame = [newLayer frame];
1136        _pluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
1137        [newLayer addSublayer:_pluginLayer.get()];
1138    }
1139}
1140
1141- (void)loadStream
1142{
1143    if ([self _shouldCancelSrcStream])
1144        return;
1145
1146    if (_loadManually) {
1147        [self _redeliverStream];
1148        return;
1149    }
1150
1151    // If the OBJECT/EMBED tag has no SRC, the URL is passed to us as "".
1152    // Check for this and don't start a load in this case.
1153    if (_sourceURL && ![_sourceURL.get() _web_isEmpty]) {
1154        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_sourceURL.get()];
1155        [request _web_setHTTPReferrer:core([self webFrame])->loader().outgoingReferrer()];
1156        [self loadRequest:request inTarget:nil withNotifyData:nil sendNotification:NO];
1157    }
1158}
1159
1160- (BOOL)shouldStop
1161{
1162    // If we're already calling a plug-in function, do not call NPP_Destroy().  The plug-in function we are calling
1163    // may assume that its instance->pdata, or other memory freed by NPP_Destroy(), is valid and unchanged until said
1164    // plugin-function returns.
1165    // See <rdar://problem/4480737>.
1166    if (pluginFunctionCallDepth > 0) {
1167        shouldStopSoon = YES;
1168        return NO;
1169    }
1170
1171    return YES;
1172}
1173
1174- (void)destroyPlugin
1175{
1176    // To stop active streams it's necessary to invoke stop() on a copy
1177    // of streams. This is because calling WebNetscapePluginStream::stop() also has the side effect
1178    // of removing a stream from this hash set.
1179    Vector<RefPtr<WebNetscapePluginStream>> streamsCopy;
1180    copyToVector(streams, streamsCopy);
1181    for (auto& stream: streamsCopy)
1182        stream->stop();
1183
1184    for (WebFrame *frame in [_pendingFrameLoads keyEnumerator])
1185        [frame _setInternalLoadDelegate:nil];
1186    [NSObject cancelPreviousPerformRequestsWithTarget:self];
1187
1188    // Setting the window type to 0 ensures that NPP_SetWindow will be called if the plug-in is restarted.
1189    lastSetWindow.type = (NPWindowType)0;
1190
1191    _pluginLayer = 0;
1192
1193    [self _destroyPlugin];
1194    [_pluginPackage.get() close];
1195
1196    _eventHandler.clear();
1197}
1198
1199- (NPEventModel)eventModel
1200{
1201    return eventModel;
1202}
1203
1204- (NPP)plugin
1205{
1206    return plugin;
1207}
1208
1209- (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values
1210{
1211    ASSERT([keys count] == [values count]);
1212
1213    // Convert the attributes to 2 C string arrays.
1214    // These arrays are passed to NPP_New, but the strings need to be
1215    // modifiable and live the entire life of the plugin.
1216
1217    // The Java plug-in requires the first argument to be the base URL
1218    if ([_MIMEType.get() isEqualToString:@"application/x-java-applet"]) {
1219        cAttributes = (char **)malloc(([keys count] + 1) * sizeof(char *));
1220        cValues = (char **)malloc(([values count] + 1) * sizeof(char *));
1221        cAttributes[0] = strdup("DOCBASE");
1222        cValues[0] = strdup([_baseURL.get() _web_URLCString]);
1223        argsCount++;
1224    } else {
1225        cAttributes = (char **)malloc([keys count] * sizeof(char *));
1226        cValues = (char **)malloc([values count] * sizeof(char *));
1227    }
1228
1229    BOOL isWMP = [_pluginPackage.get() bundleIdentifier] == "com.microsoft.WMP.defaultplugin";
1230
1231    unsigned i;
1232    unsigned count = [keys count];
1233    for (i = 0; i < count; i++) {
1234        NSString *key = [keys objectAtIndex:i];
1235        NSString *value = [values objectAtIndex:i];
1236        if ([key _webkit_isCaseInsensitiveEqualToString:@"height"]) {
1237            specifiedHeight = [value intValue];
1238        } else if ([key _webkit_isCaseInsensitiveEqualToString:@"width"]) {
1239            specifiedWidth = [value intValue];
1240        }
1241        // Avoid Window Media Player crash when these attributes are present.
1242        if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) {
1243            continue;
1244        }
1245        cAttributes[argsCount] = strdup([key UTF8String]);
1246        cValues[argsCount] = strdup([value UTF8String]);
1247        LOG(Plugins, "%@ = %@", key, value);
1248        argsCount++;
1249    }
1250}
1251
1252- (uint32_t)checkIfAllowedToLoadURL:(const char*)urlCString frame:(const char*)frameNameCString
1253                       callbackFunc:(void (*)(NPP npp, uint32_t checkID, NPBool allowed, void* context))callbackFunc
1254                            context:(void*)context
1255{
1256    if (!_containerChecksInProgress)
1257        _containerChecksInProgress = [[NSMutableDictionary alloc] init];
1258
1259    NSString *frameName = frameNameCString ? [NSString stringWithCString:frameNameCString encoding:NSISOLatin1StringEncoding] : nil;
1260
1261    ++_currentContainerCheckRequestID;
1262    WebNetscapeContainerCheckContextInfo *contextInfo = [[WebNetscapeContainerCheckContextInfo alloc] initWithCheckRequestID:_currentContainerCheckRequestID
1263                                                                                                                callbackFunc:callbackFunc
1264                                                                                                                      context:context];
1265
1266    WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:[self requestWithURLCString:urlCString]
1267                                                                        target:frameName
1268                                                                  resultObject:self
1269                                                                      selector:@selector(_containerCheckResult:contextInfo:)
1270                                                                    controller:self
1271                                                                   contextInfo:contextInfo];
1272
1273    [contextInfo release];
1274    [_containerChecksInProgress setObject:check forKey:[NSNumber numberWithInt:_currentContainerCheckRequestID]];
1275    [check start];
1276
1277    return _currentContainerCheckRequestID;
1278}
1279
1280- (void)_containerCheckResult:(PolicyAction)policy contextInfo:(id)contextInfo
1281{
1282    ASSERT([contextInfo isKindOfClass:[WebNetscapeContainerCheckContextInfo class]]);
1283    void (*pluginCallback)(NPP npp, uint32_t, NPBool, void*) = [contextInfo callback];
1284
1285    if (!pluginCallback) {
1286        ASSERT_NOT_REACHED();
1287        return;
1288    }
1289
1290    pluginCallback([self plugin], [contextInfo checkRequestID], (policy == PolicyUse), [contextInfo context]);
1291}
1292
1293- (void)cancelCheckIfAllowedToLoadURL:(uint32_t)checkID
1294{
1295    WebPluginContainerCheck *check = (WebPluginContainerCheck *)[_containerChecksInProgress objectForKey:[NSNumber numberWithInt:checkID]];
1296
1297    if (!check)
1298        return;
1299
1300    [check cancel];
1301    [_containerChecksInProgress removeObjectForKey:[NSNumber numberWithInt:checkID]];
1302}
1303
1304// WebPluginContainerCheck automatically calls this method after invoking our _containerCheckResult: selector.
1305// It works this way because calling -[WebPluginContainerCheck cancel] allows it to do it's teardown process.
1306- (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)webPluginContainerCheck
1307{
1308    ASSERT([webPluginContainerCheck isKindOfClass:[WebPluginContainerCheck class]]);
1309    WebPluginContainerCheck *check = (WebPluginContainerCheck *)webPluginContainerCheck;
1310    ASSERT([[check contextInfo] isKindOfClass:[WebNetscapeContainerCheckContextInfo class]]);
1311
1312    [self cancelCheckIfAllowedToLoadURL:[[check contextInfo] checkRequestID]];
1313}
1314
1315
1316// MARK: NSVIEW
1317
1318- (id)initWithFrame:(NSRect)frame
1319      pluginPackage:(WebNetscapePluginPackage *)pluginPackage
1320                URL:(NSURL *)URL
1321            baseURL:(NSURL *)baseURL
1322           MIMEType:(NSString *)MIME
1323      attributeKeys:(NSArray *)keys
1324    attributeValues:(NSArray *)values
1325       loadManually:(BOOL)loadManually
1326            element:(PassRefPtr<WebCore::HTMLPlugInElement>)element
1327{
1328    self = [super initWithFrame:frame pluginPackage:pluginPackage URL:URL baseURL:baseURL MIMEType:MIME attributeKeys:keys attributeValues:values loadManually:loadManually element:element];
1329    if (!self)
1330        return nil;
1331
1332    _pendingFrameLoads = adoptNS([[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsStrongMemory capacity:0]);
1333
1334    // load the plug-in if it is not already loaded
1335    if (![pluginPackage load]) {
1336        [self release];
1337        return nil;
1338    }
1339
1340    return self;
1341}
1342
1343- (id)initWithFrame:(NSRect)frame
1344{
1345    ASSERT_NOT_REACHED();
1346    return nil;
1347}
1348
1349- (void)fini
1350{
1351#ifndef NP_NO_QUICKDRAW
1352    if (offscreenGWorld)
1353        DisposeGWorld(offscreenGWorld);
1354#endif
1355
1356    for (unsigned i = 0; i < argsCount; i++) {
1357        free(cAttributes[i]);
1358        free(cValues[i]);
1359    }
1360    free(cAttributes);
1361    free(cValues);
1362
1363    ASSERT(!_eventHandler);
1364
1365    [_containerChecksInProgress release];
1366}
1367
1368- (void)disconnectStream:(WebNetscapePluginStream*)stream
1369{
1370    streams.remove(stream);
1371}
1372
1373- (void)dealloc
1374{
1375    ASSERT(!_isStarted);
1376    ASSERT(!plugin);
1377
1378    [self fini];
1379
1380    [super dealloc];
1381}
1382
1383- (void)finalize
1384{
1385    ASSERT_MAIN_THREAD();
1386    ASSERT(!_isStarted);
1387
1388    [self fini];
1389
1390    [super finalize];
1391}
1392
1393- (void)drawRect:(NSRect)rect
1394{
1395    if (_cachedSnapshot) {
1396        NSRect sourceRect = { NSZeroPoint, [_cachedSnapshot.get() size] };
1397        [_cachedSnapshot.get() drawInRect:[self bounds] fromRect:sourceRect operation:NSCompositeSourceOver fraction:1];
1398        return;
1399    }
1400
1401    if (drawingModel == NPDrawingModelCoreAnimation && (!_snapshotting || ![self supportsSnapshotting]))
1402        return;
1403
1404    if (!_isStarted)
1405        return;
1406
1407    if ([NSGraphicsContext currentContextDrawingToScreen] || _isFlash)
1408        [self sendDrawRectEvent:rect];
1409    else {
1410        NSBitmapImageRep *printedPluginBitmap = [self _printedPluginBitmap];
1411        if (printedPluginBitmap) {
1412            // Flip the bitmap before drawing because the QuickDraw port is flipped relative
1413            // to this view.
1414            CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
1415            CGContextSaveGState(cgContext);
1416            NSRect bounds = [self bounds];
1417            CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
1418            CGContextScaleCTM(cgContext, 1.0f, -1.0f);
1419            [printedPluginBitmap drawInRect:bounds];
1420            CGContextRestoreGState(cgContext);
1421        }
1422    }
1423}
1424
1425- (NPObject *)createPluginScriptableObject
1426{
1427    if (![_pluginPackage.get() pluginFuncs]->getvalue || !_isStarted)
1428        return NULL;
1429
1430    NPObject *value = NULL;
1431    NPError error;
1432    [self willCallPlugInFunction];
1433    {
1434        JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
1435        error = [_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginScriptableNPObject, &value);
1436    }
1437    [self didCallPlugInFunction];
1438    if (error != NPERR_NO_ERROR)
1439        return NULL;
1440
1441    return value;
1442}
1443
1444- (BOOL)getFormValue:(NSString **)value
1445{
1446    if (![_pluginPackage.get() pluginFuncs]->getvalue || !_isStarted)
1447        return false;
1448    // Plugins will allocate memory for the buffer by using NPN_MemAlloc().
1449    char* buffer = NULL;
1450    NPError error;
1451    [self willCallPlugInFunction];
1452    {
1453        JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
1454        error = [_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVformValue, &buffer);
1455    }
1456    [self didCallPlugInFunction];
1457    if (error != NPERR_NO_ERROR || !buffer)
1458        return false;
1459    *value = [[NSString alloc] initWithUTF8String:buffer];
1460    [_pluginPackage.get() browserFuncs]->memfree(buffer);
1461    return true;
1462}
1463
1464- (void)willCallPlugInFunction
1465{
1466    ASSERT(plugin);
1467
1468    // Could try to prevent infinite recursion here, but it's probably not worth the effort.
1469    pluginFunctionCallDepth++;
1470}
1471
1472- (void)didCallPlugInFunction
1473{
1474    ASSERT(pluginFunctionCallDepth > 0);
1475    pluginFunctionCallDepth--;
1476
1477    // If -stop was called while we were calling into a plug-in function, and we're no longer
1478    // inside a plug-in function, stop now.
1479    if (pluginFunctionCallDepth == 0 && shouldStopSoon) {
1480        shouldStopSoon = NO;
1481        [self stop];
1482    }
1483}
1484
1485-(void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
1486{
1487    ASSERT(_loadManually);
1488    ASSERT(!_manualStream);
1489
1490    _manualStream = WebNetscapePluginStream::create(&core([self webFrame])->loader());
1491}
1492
1493- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
1494{
1495    ASSERT(_loadManually);
1496    ASSERT(_manualStream);
1497
1498    _dataLengthReceived += [data length];
1499
1500    if (!_isStarted)
1501        return;
1502
1503    if (!_manualStream->plugin()) {
1504        // Check if the load should be cancelled
1505        if ([self _shouldCancelSrcStream]) {
1506            NSURLResponse *response = [[self dataSource] response];
1507
1508            NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad
1509                                                            contentURL:[response URL]
1510                                                         pluginPageURL:nil
1511                                                            pluginName:nil // FIXME: Get this from somewhere
1512                                                              MIMEType:[response MIMEType]];
1513            [[self dataSource] _documentLoader]->cancelMainResourceLoad(error);
1514            [error release];
1515            return;
1516        }
1517
1518        _manualStream->setRequestURL([[[self dataSource] request] URL]);
1519        _manualStream->setPlugin([self plugin]);
1520        ASSERT(_manualStream->plugin());
1521
1522        _manualStream->startStreamWithResponse([[self dataSource] response]);
1523    }
1524
1525    if (_manualStream->plugin())
1526        _manualStream->didReceiveData(0, static_cast<const char *>([data bytes]), [data length]);
1527}
1528
1529- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
1530{
1531    ASSERT(_loadManually);
1532
1533    _error = error;
1534
1535    if (!_isStarted) {
1536        return;
1537    }
1538
1539    _manualStream->destroyStreamWithError(error);
1540}
1541
1542- (void)pluginViewFinishedLoading:(NSView *)pluginView
1543{
1544    ASSERT(_loadManually);
1545    ASSERT(_manualStream);
1546
1547    if (_isStarted)
1548        _manualStream->didFinishLoading(0);
1549}
1550
1551- (NSTextInputContext *)inputContext
1552{
1553    return nil;
1554}
1555
1556@end
1557
1558@implementation WebNetscapePluginView (WebNPPCallbacks)
1559
1560- (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest
1561{
1562    // FIXME: Is this isStarted check needed here? evaluateJavaScriptPluginRequest should not be called
1563    // if we are stopped since this method is called after a delay and we call
1564    // cancelPreviousPerformRequestsWithTarget inside of stop.
1565    if (!_isStarted) {
1566        return;
1567    }
1568
1569    NSURL *URL = [[JSPluginRequest request] URL];
1570    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1571    ASSERT(JSString);
1572
1573    NSString *result = [[self webFrame] _stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]];
1574
1575    // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop.
1576    if (!_isStarted) {
1577        return;
1578    }
1579
1580    if ([JSPluginRequest frameName] != nil) {
1581        // FIXME: If the result is a string, we probably want to put that string into the frame.
1582        if ([JSPluginRequest sendNotification]) {
1583            [self willCallPlugInFunction];
1584            {
1585                JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
1586                [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]);
1587            }
1588            [self didCallPlugInFunction];
1589        }
1590    } else if ([result length] > 0) {
1591        // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does.
1592        NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding];
1593
1594        RefPtr<WebNetscapePluginStream> stream = WebNetscapePluginStream::create([NSURLRequest requestWithURL:URL], plugin, [JSPluginRequest sendNotification], [JSPluginRequest notifyData]);
1595
1596        RetainPtr<NSURLResponse> response = adoptNS([[NSURLResponse alloc] initWithURL:URL
1597                                                                             MIMEType:@"text/plain"
1598                                                                expectedContentLength:[JSData length]
1599                                                                     textEncodingName:nil]);
1600
1601        stream->startStreamWithResponse(response.get());
1602        stream->didReceiveData(0, static_cast<const char*>([JSData bytes]), [JSData length]);
1603        stream->didFinishLoading(0);
1604    }
1605}
1606
1607- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
1608{
1609    ASSERT(_isStarted);
1610
1611    WebPluginRequest *pluginRequest = [_pendingFrameLoads objectForKey:webFrame];
1612    ASSERT(pluginRequest != nil);
1613    ASSERT([pluginRequest sendNotification]);
1614
1615    [self willCallPlugInFunction];
1616    {
1617        JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
1618        [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]);
1619    }
1620    [self didCallPlugInFunction];
1621
1622    [_pendingFrameLoads removeObjectForKey:webFrame];
1623    [webFrame _setInternalLoadDelegate:nil];
1624}
1625
1626- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
1627{
1628    NPReason reason = NPRES_DONE;
1629    if (error != nil)
1630        reason = WebNetscapePluginStream::reasonForError(error);
1631    [self webFrame:webFrame didFinishLoadWithReason:reason];
1632}
1633
1634- (void)loadPluginRequest:(WebPluginRequest *)pluginRequest
1635{
1636    NSURLRequest *request = [pluginRequest request];
1637    NSString *frameName = [pluginRequest frameName];
1638    WebFrame *frame = nil;
1639
1640    NSURL *URL = [request URL];
1641    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1642
1643    ASSERT(frameName || JSString);
1644
1645    if (frameName) {
1646        // FIXME - need to get rid of this window creation which
1647        // bypasses normal targeted link handling
1648        frame = kit(core([self webFrame])->loader().findFrameForNavigation(frameName));
1649        if (frame == nil) {
1650            WebView *currentWebView = [self webView];
1651            NSDictionary *features = [[NSDictionary alloc] init];
1652            WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView
1653                                                        createWebViewWithRequest:nil
1654                                                                  windowFeatures:features];
1655            [features release];
1656
1657            if (!newWebView) {
1658                if ([pluginRequest sendNotification]) {
1659                    [self willCallPlugInFunction];
1660                    {
1661                        JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
1662                        [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [[[pluginRequest request] URL] _web_URLCString], NPERR_GENERIC_ERROR, [pluginRequest notifyData]);
1663                    }
1664                    [self didCallPlugInFunction];
1665                }
1666                return;
1667            }
1668
1669            frame = [newWebView mainFrame];
1670            core(frame)->tree().setName(frameName);
1671            [[newWebView _UIDelegateForwarder] webViewShow:newWebView];
1672        }
1673    }
1674
1675    if (JSString) {
1676        ASSERT(frame == nil || [self webFrame] == frame);
1677        [self evaluateJavaScriptPluginRequest:pluginRequest];
1678    } else {
1679        [frame loadRequest:request];
1680        if ([pluginRequest sendNotification]) {
1681            // Check if another plug-in view or even this view is waiting for the frame to load.
1682            // If it is, tell it that the load was cancelled because it will be anyway.
1683            WebNetscapePluginView *view = [frame _internalLoadDelegate];
1684            if (view != nil) {
1685                ASSERT([view isKindOfClass:[WebNetscapePluginView class]]);
1686                [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK];
1687            }
1688            [_pendingFrameLoads setObject:pluginRequest forKey:frame];
1689            [frame _setInternalLoadDelegate:self];
1690        }
1691    }
1692}
1693
1694- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification
1695{
1696    NSURL *URL = [request URL];
1697
1698    if (!URL)
1699        return NPERR_INVALID_URL;
1700
1701    // Don't allow requests to be loaded when the document loader is stopping all loaders.
1702    if ([[self dataSource] _documentLoader]->isStopping())
1703        return NPERR_GENERIC_ERROR;
1704
1705    NSString *target = nil;
1706    if (cTarget) {
1707        // Find the frame given the target string.
1708        target = [NSString stringWithCString:cTarget encoding:NSISOLatin1StringEncoding];
1709    }
1710    WebFrame *frame = [self webFrame];
1711
1712    // don't let a plugin start any loads if it is no longer part of a document that is being
1713    // displayed unless the loads are in the same frame as the plugin.
1714    if ([[self dataSource] _documentLoader] != core([self webFrame])->loader().activeDocumentLoader() &&
1715        (!cTarget || [frame findFrameNamed:target] != frame)) {
1716        return NPERR_GENERIC_ERROR;
1717    }
1718
1719    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
1720    if (JSString != nil) {
1721        if (![[[self webView] preferences] isJavaScriptEnabled]) {
1722            // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does.
1723            return NPERR_GENERIC_ERROR;
1724        } else if (cTarget == NULL && _mode == NP_FULL) {
1725            // Don't allow a JavaScript request from a standalone plug-in that is self-targetted
1726            // because this can cause the user to be redirected to a blank page (3424039).
1727            return NPERR_INVALID_PARAM;
1728        }
1729    } else {
1730        if (!core([self webFrame])->document()->securityOrigin()->canDisplay(URL))
1731            return NPERR_GENERIC_ERROR;
1732    }
1733
1734    if (cTarget || JSString) {
1735        // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't
1736        // want to potentially kill the plug-in inside of its URL request.
1737
1738        if (JSString && target && [frame findFrameNamed:target] != frame) {
1739            // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
1740            return NPERR_INVALID_PARAM;
1741        }
1742
1743        bool currentEventIsUserGesture = false;
1744        if (_eventHandler)
1745            currentEventIsUserGesture = _eventHandler->currentEventIsUserGesture();
1746
1747        WebPluginRequest *pluginRequest = [[WebPluginRequest alloc] initWithRequest:request
1748                                                                          frameName:target
1749                                                                         notifyData:notifyData
1750                                                                   sendNotification:sendNotification
1751                                                            didStartFromUserGesture:currentEventIsUserGesture];
1752        [self performSelector:@selector(loadPluginRequest:) withObject:pluginRequest afterDelay:0];
1753        [pluginRequest release];
1754    } else {
1755        RefPtr<WebNetscapePluginStream> stream = WebNetscapePluginStream::create(request, plugin, sendNotification, notifyData);
1756
1757        streams.add(stream.get());
1758        stream->start();
1759    }
1760
1761    return NPERR_NO_ERROR;
1762}
1763
1764-(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData
1765{
1766    LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget);
1767
1768    NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1769    return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES];
1770}
1771
1772-(NPError)getURL:(const char *)URLCString target:(const char *)cTarget
1773{
1774    LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget);
1775
1776    NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1777    return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO];
1778}
1779
1780- (NPError)_postURL:(const char *)URLCString
1781             target:(const char *)target
1782                len:(UInt32)len
1783                buf:(const char *)buf
1784               file:(NPBool)file
1785         notifyData:(void *)notifyData
1786   sendNotification:(BOOL)sendNotification
1787       allowHeaders:(BOOL)allowHeaders
1788{
1789    if (!URLCString || !len || !buf) {
1790        return NPERR_INVALID_PARAM;
1791    }
1792
1793    NSData *postData = nil;
1794
1795    if (file) {
1796        // If we're posting a file, buf is either a file URL or a path to the file.
1797        NSString *bufString = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingWindowsLatin1);
1798        if (!bufString) {
1799            return NPERR_INVALID_PARAM;
1800        }
1801        NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString];
1802        NSString *path;
1803        if ([fileURL isFileURL]) {
1804            path = [fileURL path];
1805        } else {
1806            path = bufString;
1807        }
1808        postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]];
1809        CFRelease(bufString);
1810        if (!postData) {
1811            return NPERR_FILE_NOT_FOUND;
1812        }
1813    } else {
1814        postData = [NSData dataWithBytes:buf length:len];
1815    }
1816
1817    if ([postData length] == 0) {
1818        return NPERR_INVALID_PARAM;
1819    }
1820
1821    NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
1822    [request setHTTPMethod:@"POST"];
1823
1824    if (allowHeaders) {
1825        if ([postData _web_startsWithBlankLine]) {
1826            postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)];
1827        } else {
1828            NSInteger location = [postData _web_locationAfterFirstBlankLine];
1829            if (location != NSNotFound) {
1830                // If the blank line is somewhere in the middle of postData, everything before is the header.
1831                NSData *headerData = [postData subdataWithRange:NSMakeRange(0, location)];
1832                NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields];
1833                unsigned dataLength = [postData length] - location;
1834
1835                // Sometimes plugins like to set Content-Length themselves when they post,
1836                // but WebFoundation does not like that. So we will remove the header
1837                // and instead truncate the data to the requested length.
1838                NSString *contentLength = [header objectForKey:@"Content-Length"];
1839
1840                if (contentLength != nil)
1841                    dataLength = std::min<unsigned>([contentLength intValue], dataLength);
1842                [header removeObjectForKey:@"Content-Length"];
1843
1844                if ([header count] > 0) {
1845                    [request setAllHTTPHeaderFields:header];
1846                }
1847                // Everything after the blank line is the actual content of the POST.
1848                postData = [postData subdataWithRange:NSMakeRange(location, dataLength)];
1849
1850            }
1851        }
1852        if ([postData length] == 0) {
1853            return NPERR_INVALID_PARAM;
1854        }
1855    }
1856
1857    // Plug-ins expect to receive uncached data when doing a POST (3347134).
1858    [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
1859    [request setHTTPBody:postData];
1860
1861    return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification];
1862}
1863
1864- (NPError)postURLNotify:(const char *)URLCString
1865                  target:(const char *)target
1866                     len:(UInt32)len
1867                     buf:(const char *)buf
1868                    file:(NPBool)file
1869              notifyData:(void *)notifyData
1870{
1871    LOG(Plugins, "NPN_PostURLNotify: %s", URLCString);
1872    return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:notifyData sendNotification:YES allowHeaders:YES];
1873}
1874
1875-(NPError)postURL:(const char *)URLCString
1876           target:(const char *)target
1877              len:(UInt32)len
1878              buf:(const char *)buf
1879             file:(NPBool)file
1880{
1881    LOG(Plugins, "NPN_PostURL: %s", URLCString);
1882    // As documented, only allow headers to be specified via NPP_PostURL when using a file.
1883    return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:NULL sendNotification:NO allowHeaders:file];
1884}
1885
1886-(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream
1887{
1888    LOG(Plugins, "NPN_NewStream");
1889    return NPERR_GENERIC_ERROR;
1890}
1891
1892-(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer
1893{
1894    LOG(Plugins, "NPN_Write");
1895    return NPERR_GENERIC_ERROR;
1896}
1897
1898-(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason
1899{
1900    LOG(Plugins, "NPN_DestroyStream");
1901    // This function does a sanity check to ensure that the NPStream provided actually
1902    // belongs to the plug-in that provided it, which fixes a crash in the DivX
1903    // plug-in: <rdar://problem/5093862> | http://bugs.webkit.org/show_bug.cgi?id=13203
1904    if (!stream || WebNetscapePluginStream::ownerForStream(stream) != plugin) {
1905        LOG(Plugins, "Invalid NPStream passed to NPN_DestroyStream: %p", stream);
1906        return NPERR_INVALID_INSTANCE_ERROR;
1907    }
1908
1909    WebNetscapePluginStream* browserStream = static_cast<WebNetscapePluginStream*>(stream->ndata);
1910    browserStream->cancelLoadAndDestroyStreamWithError(browserStream->errorForReason(reason));
1911
1912    return NPERR_NO_ERROR;
1913}
1914
1915- (const char *)userAgent
1916{
1917    NSString *userAgent = [[self webView] userAgentForURL:_baseURL.get()];
1918
1919    if (_isSilverlight) {
1920        // Silverlight has a workaround for a leak in Safari 2. This workaround is
1921        // applied when the user agent does not contain "Version/3" so we append it
1922        // at the end of the user agent.
1923        userAgent = [userAgent stringByAppendingString:@" Version/3.2.1"];
1924    }
1925
1926    return [userAgent UTF8String];
1927}
1928
1929-(void)status:(const char *)message
1930{
1931    CFStringRef status = CFStringCreateWithCString(NULL, message ? message : "", kCFStringEncodingUTF8);
1932    if (!status) {
1933        LOG_ERROR("NPN_Status: the message was not valid UTF-8");
1934        return;
1935    }
1936
1937    LOG(Plugins, "NPN_Status: %@", status);
1938    WebView *wv = [self webView];
1939    [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status];
1940    CFRelease(status);
1941}
1942
1943-(void)invalidateRect:(NPRect *)invalidRect
1944{
1945    LOG(Plugins, "NPN_InvalidateRect");
1946    [self invalidatePluginContentRect:NSMakeRect(invalidRect->left, invalidRect->top,
1947        (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)];
1948}
1949
1950- (void)invalidateRegion:(NPRegion)invalidRegion
1951{
1952    LOG(Plugins, "NPN_InvalidateRegion");
1953    NSRect invalidRect = NSZeroRect;
1954    switch (drawingModel) {
1955#ifndef NP_NO_QUICKDRAW
1956        case NPDrawingModelQuickDraw:
1957        {
1958            ::Rect qdRect;
1959            GetRegionBounds((NPQDRegion)invalidRegion, &qdRect);
1960            invalidRect = NSMakeRect(qdRect.left, qdRect.top, qdRect.right - qdRect.left, qdRect.bottom - qdRect.top);
1961        }
1962        break;
1963#endif /* NP_NO_QUICKDRAW */
1964
1965        case NPDrawingModelCoreGraphics:
1966        {
1967            CGRect cgRect = CGPathGetBoundingBox((NPCGRegion)invalidRegion);
1968            invalidRect = *(NSRect*)&cgRect;
1969            break;
1970        }
1971        default:
1972            ASSERT_NOT_REACHED();
1973        break;
1974    }
1975
1976    [self invalidatePluginContentRect:invalidRect];
1977}
1978
1979-(void)forceRedraw
1980{
1981    LOG(Plugins, "forceRedraw");
1982    [self invalidatePluginContentRect:[self bounds]];
1983    [[self window] displayIfNeeded];
1984}
1985
1986- (NPError)getVariable:(NPNVariable)variable value:(void *)value
1987{
1988    switch (static_cast<unsigned>(variable)) {
1989        case NPNVWindowNPObject:
1990        {
1991            Frame* frame = core([self webFrame]);
1992            NPObject* windowScriptObject = frame ? frame->script().windowScriptNPObject() : 0;
1993
1994            // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
1995            if (windowScriptObject)
1996                _NPN_RetainObject(windowScriptObject);
1997
1998            void **v = (void **)value;
1999            *v = windowScriptObject;
2000
2001            return NPERR_NO_ERROR;
2002        }
2003
2004        case NPNVPluginElementNPObject:
2005        {
2006            if (!_element)
2007                return NPERR_GENERIC_ERROR;
2008
2009            NPObject *plugInScriptObject = _element->getNPObject();
2010
2011            // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
2012            if (plugInScriptObject)
2013                _NPN_RetainObject(plugInScriptObject);
2014
2015            void **v = (void **)value;
2016            *v = plugInScriptObject;
2017
2018            return NPERR_NO_ERROR;
2019        }
2020
2021        case NPNVpluginDrawingModel:
2022        {
2023            *(NPDrawingModel *)value = drawingModel;
2024            return NPERR_NO_ERROR;
2025        }
2026
2027#ifndef NP_NO_QUICKDRAW
2028        case NPNVsupportsQuickDrawBool:
2029        {
2030            *(NPBool *)value = TRUE;
2031            return NPERR_NO_ERROR;
2032        }
2033#endif /* NP_NO_QUICKDRAW */
2034
2035        case NPNVsupportsCoreGraphicsBool:
2036        {
2037            *(NPBool *)value = TRUE;
2038            return NPERR_NO_ERROR;
2039        }
2040
2041        case NPNVsupportsOpenGLBool:
2042        {
2043            *(NPBool *)value = FALSE;
2044            return NPERR_NO_ERROR;
2045        }
2046
2047        case NPNVsupportsCoreAnimationBool:
2048        {
2049            *(NPBool *)value = TRUE;
2050            return NPERR_NO_ERROR;
2051        }
2052
2053#ifndef NP_NO_CARBON
2054        case NPNVsupportsCarbonBool:
2055        {
2056            *(NPBool *)value = TRUE;
2057            return NPERR_NO_ERROR;
2058        }
2059#endif /* NP_NO_CARBON */
2060
2061        case NPNVsupportsCocoaBool:
2062        {
2063            *(NPBool *)value = TRUE;
2064            return NPERR_NO_ERROR;
2065        }
2066
2067        case NPNVprivateModeBool:
2068        {
2069            *(NPBool *)value = _isPrivateBrowsingEnabled;
2070            return NPERR_NO_ERROR;
2071        }
2072
2073        case WKNVBrowserContainerCheckFuncs:
2074        {
2075            *(WKNBrowserContainerCheckFuncs **)value = browserContainerCheckFuncs();
2076            return NPERR_NO_ERROR;
2077        }
2078
2079        case WKNVSupportsCompositingCoreAnimationPluginsBool:
2080        {
2081            *(NPBool *)value = [[[self webView] preferences] acceleratedCompositingEnabled];
2082            return NPERR_NO_ERROR;
2083        }
2084
2085        default:
2086            break;
2087    }
2088
2089    return NPERR_GENERIC_ERROR;
2090}
2091
2092- (NPError)setVariable:(NPPVariable)variable value:(void *)value
2093{
2094    switch (variable) {
2095        case NPPVpluginDrawingModel:
2096        {
2097            // Can only set drawing model inside NPP_New()
2098            if (self != [[self class] currentPluginView])
2099                return NPERR_GENERIC_ERROR;
2100
2101            // Check for valid, supported drawing model
2102            NPDrawingModel newDrawingModel = (NPDrawingModel)(uintptr_t)value;
2103            switch (newDrawingModel) {
2104                // Supported drawing models:
2105#ifndef NP_NO_QUICKDRAW
2106                case NPDrawingModelQuickDraw:
2107#endif
2108                case NPDrawingModelCoreGraphics:
2109                case NPDrawingModelCoreAnimation:
2110                    drawingModel = newDrawingModel;
2111                    return NPERR_NO_ERROR;
2112
2113
2114                // Unsupported (or unknown) drawing models:
2115                default:
2116                    LOG(Plugins, "Plugin %@ uses unsupported drawing model: %d", _eventHandler.get(), drawingModel);
2117                    return NPERR_GENERIC_ERROR;
2118            }
2119        }
2120
2121        case NPPVpluginEventModel:
2122        {
2123            // Can only set event model inside NPP_New()
2124            if (self != [[self class] currentPluginView])
2125                return NPERR_GENERIC_ERROR;
2126
2127            // Check for valid, supported event model
2128            NPEventModel newEventModel = (NPEventModel)(uintptr_t)value;
2129            switch (newEventModel) {
2130                // Supported event models:
2131#ifndef NP_NO_CARBON
2132                case NPEventModelCarbon:
2133#endif
2134                case NPEventModelCocoa:
2135                    eventModel = newEventModel;
2136                    return NPERR_NO_ERROR;
2137
2138                    // Unsupported (or unknown) event models:
2139                default:
2140                    LOG(Plugins, "Plugin %@ uses unsupported event model: %d", _eventHandler.get(), eventModel);
2141                    return NPERR_GENERIC_ERROR;
2142            }
2143        }
2144
2145        default:
2146            return NPERR_GENERIC_ERROR;
2147    }
2148}
2149
2150- (uint32_t)scheduleTimerWithInterval:(uint32_t)interval repeat:(NPBool)repeat timerFunc:(void (*)(NPP npp, uint32_t timerID))timerFunc
2151{
2152    if (!timerFunc)
2153        return 0;
2154
2155    if (!timers)
2156        timers = std::make_unique<HashMap<uint32_t, std::unique_ptr<PluginTimer>>>();
2157
2158    std::unique_ptr<PluginTimer>* slot;
2159    uint32_t timerID;
2160    do
2161        timerID = ++currentTimerID;
2162    while (!timers->isValidKey(timerID) || *(slot = &timers->add(timerID, nullptr).iterator->value));
2163
2164    auto timer = std::make_unique<PluginTimer>(plugin, timerID, interval, repeat, timerFunc);
2165
2166    if (_shouldFireTimers)
2167        timer->start(_isCompletelyObscured);
2168
2169    *slot = WTF::move(timer);
2170
2171    return timerID;
2172}
2173
2174- (void)unscheduleTimer:(uint32_t)timerID
2175{
2176    if (!timers)
2177        return;
2178
2179    timers->remove(timerID);
2180}
2181
2182- (NPError)popUpContextMenu:(NPMenu *)menu
2183{
2184    NSEvent *currentEvent = [NSApp currentEvent];
2185
2186    // NPN_PopUpContextMenu must be called from within the plug-in's NPP_HandleEvent.
2187    if (!currentEvent)
2188        return NPERR_GENERIC_ERROR;
2189
2190    [NSMenu popUpContextMenu:(NSMenu *)menu withEvent:currentEvent forView:self];
2191    return NPERR_NO_ERROR;
2192}
2193
2194- (NPError)getVariable:(NPNURLVariable)variable forURL:(const char*)url value:(char**)value length:(uint32_t*)length
2195{
2196    switch (variable) {
2197        case NPNURLVCookie: {
2198            if (!value)
2199                break;
2200
2201            NSURL *URL = [self URLWithCString:url];
2202            if (!URL)
2203                break;
2204
2205            if (Frame* frame = core([self webFrame])) {
2206                String cookieString = cookies(frame->document(), URL);
2207                CString cookieStringUTF8 = cookieString.utf8();
2208                if (cookieStringUTF8.isNull())
2209                    return NPERR_GENERIC_ERROR;
2210
2211                *value = static_cast<char*>(NPN_MemAlloc(cookieStringUTF8.length()));
2212                memcpy(*value, cookieStringUTF8.data(), cookieStringUTF8.length());
2213
2214                if (length)
2215                    *length = cookieStringUTF8.length();
2216                return NPERR_NO_ERROR;
2217            }
2218            break;
2219        }
2220        case NPNURLVProxy: {
2221            if (!value)
2222                break;
2223
2224            NSURL *URL = [self URLWithCString:url];
2225            if (!URL)
2226                break;
2227
2228            Vector<ProxyServer> proxyServers = proxyServersForURL(URL, 0);
2229            CString proxiesUTF8 = toString(proxyServers).utf8();
2230
2231            *value = static_cast<char*>(NPN_MemAlloc(proxiesUTF8.length()));
2232            memcpy(*value, proxiesUTF8.data(), proxiesUTF8.length());
2233
2234           if (length)
2235               *length = proxiesUTF8.length();
2236
2237            return NPERR_NO_ERROR;
2238        }
2239    }
2240    return NPERR_GENERIC_ERROR;
2241}
2242
2243- (NPError)setVariable:(NPNURLVariable)variable forURL:(const char*)url value:(const char*)value length:(uint32_t)length
2244{
2245    switch (variable) {
2246        case NPNURLVCookie: {
2247            NSURL *URL = [self URLWithCString:url];
2248            if (!URL)
2249                break;
2250
2251            String cookieString = String::fromUTF8(value, length);
2252            if (!cookieString)
2253                break;
2254
2255            if (Frame* frame = core([self webFrame])) {
2256                setCookies(frame->document(), URL, cookieString);
2257                return NPERR_NO_ERROR;
2258            }
2259
2260            break;
2261        }
2262        case NPNURLVProxy:
2263            // Can't set the proxy for a URL.
2264            break;
2265    }
2266    return NPERR_GENERIC_ERROR;
2267}
2268
2269- (NPError)getAuthenticationInfoWithProtocol:(const char*)protocolStr host:(const char*)hostStr port:(int32_t)port scheme:(const char*)schemeStr realm:(const char*)realmStr
2270                                    username:(char**)usernameStr usernameLength:(uint32_t*)usernameLength
2271                                    password:(char**)passwordStr passwordLength:(uint32_t*)passwordLength
2272{
2273    if (!protocolStr || !hostStr || !schemeStr || !realmStr || !usernameStr || !usernameLength || !passwordStr || !passwordLength)
2274        return NPERR_GENERIC_ERROR;
2275
2276    CString username;
2277    CString password;
2278    if (!getAuthenticationInfo(protocolStr, hostStr, port, schemeStr, realmStr, username, password))
2279        return NPERR_GENERIC_ERROR;
2280
2281    *usernameLength = username.length();
2282    *usernameStr = static_cast<char*>(NPN_MemAlloc(username.length()));
2283    memcpy(*usernameStr, username.data(), username.length());
2284
2285    *passwordLength = password.length();
2286    *passwordStr = static_cast<char*>(NPN_MemAlloc(password.length()));
2287    memcpy(*passwordStr, password.data(), password.length());
2288
2289    return NPERR_NO_ERROR;
2290}
2291
2292- (char*)resolveURL:(const char*)url forTarget:(const char*)target
2293{
2294    CString location = [self resolvedURLStringForURL:url target:target];
2295
2296    if (location.isNull())
2297        return 0;
2298
2299    // We use strdup here because the caller needs to free it with NPN_MemFree (which calls free).
2300    return strdup(location.data());
2301}
2302
2303@end
2304
2305@implementation WebNetscapePluginView (Internal)
2306
2307- (BOOL)_shouldCancelSrcStream
2308{
2309    ASSERT(_isStarted);
2310
2311    // Check if we should cancel the load
2312    NPBool cancelSrcStream = 0;
2313    if ([_pluginPackage.get() pluginFuncs]->getvalue &&
2314        [_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginCancelSrcStream, &cancelSrcStream) == NPERR_NO_ERROR && cancelSrcStream)
2315        return YES;
2316
2317    return NO;
2318}
2319
2320// Work around Silverlight full screen performance issue by maintaining an accelerated GL pixel format.
2321// We can safely remove it at some point in the future when both:
2322// 1) Microsoft releases a genuine fix for 7288546.
2323// 2) Enough Silverlight users update to the new Silverlight.
2324// For now, we'll distinguish older broken versions of Silverlight by asking the plug-in if it resolved its full screen badness.
2325- (void)_workaroundSilverlightFullscreenBug:(BOOL)initializedPlugin
2326{
2327    ASSERT(_isSilverlight);
2328    NPBool isFullscreenPerformanceIssueFixed = 0;
2329    NPPluginFuncs *pluginFuncs = [_pluginPackage.get() pluginFuncs];
2330    if (pluginFuncs->getvalue && pluginFuncs->getvalue(plugin, static_cast<NPPVariable>(WKNVSilverlightFullscreenPerformanceIssueFixed), &isFullscreenPerformanceIssueFixed) == NPERR_NO_ERROR && isFullscreenPerformanceIssueFixed)
2331        return;
2332
2333    static CGLPixelFormatObj pixelFormatObject = 0;
2334    static unsigned refCount = 0;
2335
2336    if (initializedPlugin) {
2337        refCount++;
2338        if (refCount == 1) {
2339            const CGLPixelFormatAttribute attributes[] = { kCGLPFAAccelerated, static_cast<CGLPixelFormatAttribute>(0) };
2340            GLint npix;
2341            CGLChoosePixelFormat(attributes, &pixelFormatObject, &npix);
2342        }
2343    } else {
2344        ASSERT(pixelFormatObject);
2345        refCount--;
2346        if (!refCount)
2347            CGLReleasePixelFormat(pixelFormatObject);
2348    }
2349}
2350
2351- (NPError)_createPlugin
2352{
2353    plugin = (NPP)calloc(1, sizeof(NPP_t));
2354    plugin->ndata = self;
2355
2356    ASSERT([_pluginPackage.get() pluginFuncs]->newp);
2357
2358    // NPN_New(), which creates the plug-in instance, should never be called while calling a plug-in function for that instance.
2359    ASSERT(pluginFunctionCallDepth == 0);
2360
2361    PluginMainThreadScheduler::scheduler().registerPlugin(plugin);
2362
2363    _isFlash = [_pluginPackage.get() bundleIdentifier] == "com.macromedia.Flash Player.plugin";
2364    _isSilverlight = [_pluginPackage.get() bundleIdentifier] == "com.microsoft.SilverlightPlugin";
2365
2366    [[self class] setCurrentPluginView:self];
2367    NPError npErr = [_pluginPackage.get() pluginFuncs]->newp((char *)[_MIMEType.get() cString], plugin, _mode, argsCount, cAttributes, cValues, NULL);
2368    [[self class] setCurrentPluginView:nil];
2369    if (_isSilverlight)
2370        [self _workaroundSilverlightFullscreenBug:YES];
2371    LOG(Plugins, "NPP_New: %d", npErr);
2372    return npErr;
2373}
2374
2375- (void)_destroyPlugin
2376{
2377    PluginMainThreadScheduler::scheduler().unregisterPlugin(plugin);
2378
2379    if (_isSilverlight)
2380        [self _workaroundSilverlightFullscreenBug:NO];
2381
2382    NPError npErr;
2383    npErr = ![_pluginPackage.get() pluginFuncs]->destroy(plugin, NULL);
2384    LOG(Plugins, "NPP_Destroy: %d", npErr);
2385
2386    if (Frame* frame = core([self webFrame]))
2387        frame->script().cleanupScriptObjectsForPlugin(self);
2388
2389    free(plugin);
2390    plugin = NULL;
2391}
2392
2393- (NSBitmapImageRep *)_printedPluginBitmap
2394{
2395#ifdef NP_NO_QUICKDRAW
2396    return nil;
2397#else
2398    // Cannot print plugins that do not implement NPP_Print
2399    if (![_pluginPackage.get() pluginFuncs]->print)
2400        return nil;
2401
2402    // This NSBitmapImageRep will share its bitmap buffer with a GWorld that the plugin will draw into.
2403    // The bitmap is created in 32-bits-per-pixel ARGB format, which is the default GWorld pixel format.
2404    NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
2405                                                         pixelsWide:window.width
2406                                                         pixelsHigh:window.height
2407                                                         bitsPerSample:8
2408                                                         samplesPerPixel:4
2409                                                         hasAlpha:YES
2410                                                         isPlanar:NO
2411                                                         colorSpaceName:NSDeviceRGBColorSpace
2412                                                         bitmapFormat:NSAlphaFirstBitmapFormat
2413                                                         bytesPerRow:0
2414                                                         bitsPerPixel:0] autorelease];
2415    ASSERT(bitmap);
2416
2417    // Create a GWorld with the same underlying buffer into which the plugin can draw
2418    ::Rect printGWorldBounds;
2419    SetRect(&printGWorldBounds, 0, 0, window.width, window.height);
2420    GWorldPtr printGWorld;
2421    if (NewGWorldFromPtr(&printGWorld,
2422                         k32ARGBPixelFormat,
2423                         &printGWorldBounds,
2424                         NULL,
2425                         NULL,
2426                         0,
2427                         (Ptr)[bitmap bitmapData],
2428                         [bitmap bytesPerRow]) != noErr) {
2429        LOG_ERROR("Could not create GWorld for printing");
2430        return nil;
2431    }
2432
2433    /// Create NPWindow for the GWorld
2434    NPWindow printNPWindow;
2435    printNPWindow.window = &printGWorld; // Normally this is an NP_Port, but when printing it is the actual CGrafPtr
2436    printNPWindow.x = 0;
2437    printNPWindow.y = 0;
2438    printNPWindow.width = window.width;
2439    printNPWindow.height = window.height;
2440    printNPWindow.clipRect.top = 0;
2441    printNPWindow.clipRect.left = 0;
2442    printNPWindow.clipRect.right = window.width;
2443    printNPWindow.clipRect.bottom = window.height;
2444    printNPWindow.type = NPWindowTypeDrawable; // Offscreen graphics port as opposed to a proper window
2445
2446    // Create embed-mode NPPrint
2447    NPPrint npPrint;
2448    npPrint.mode = NP_EMBED;
2449    npPrint.print.embedPrint.window = printNPWindow;
2450    npPrint.print.embedPrint.platformPrint = printGWorld;
2451
2452    // Tell the plugin to print into the GWorld
2453    [self willCallPlugInFunction];
2454    {
2455        JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
2456        [_pluginPackage.get() pluginFuncs]->print(plugin, &npPrint);
2457    }
2458    [self didCallPlugInFunction];
2459
2460    // Don't need the GWorld anymore
2461    DisposeGWorld(printGWorld);
2462
2463    return bitmap;
2464#endif
2465}
2466
2467- (void)_redeliverStream
2468{
2469    if ([self dataSource] && _isStarted) {
2470        // Deliver what has not been passed to the plug-in up to this point.
2471        if (_dataLengthReceived > 0) {
2472            NSData *data = [[[self dataSource] data] subdataWithRange:NSMakeRange(0, _dataLengthReceived)];
2473            _dataLengthReceived = 0;
2474            [self pluginView:self receivedData:data];
2475            if (![[self dataSource] isLoading]) {
2476                if (_error)
2477                    [self pluginView:self receivedError:_error.get()];
2478                else
2479                    [self pluginViewFinishedLoading:self];
2480            }
2481        }
2482    }
2483}
2484
2485@end
2486
2487#endif
2488