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