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