~ubuntu-branches/ubuntu/maverick/webkit/maverick

« back to all changes in this revision

Viewing changes to WebKit/Plugins/WebBaseNetscapePluginView.mm

  • Committer: Bazaar Package Importer
  • Author(s): Mike Hommey
  • Date: 2007-08-19 15:54:12 UTC
  • Revision ID: james.westby@ubuntu.com-20070819155412-uxxg1h9plpghmtbi
Tags: upstream-0~svn25144
ImportĀ upstreamĀ versionĀ 0~svn25144

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#ifndef __LP64__
 
30
 
 
31
#import "WebBaseNetscapePluginView.h"
 
32
 
 
33
#import "WebDataSourceInternal.h"
 
34
#import "WebDefaultUIDelegate.h"
 
35
#import "WebFrameBridge.h"
 
36
#import "WebFrameInternal.h" 
 
37
#import "WebFrameView.h"
 
38
#import "WebGraphicsExtras.h"
 
39
#import "WebKitLogging.h"
 
40
#import "WebKitNSStringExtras.h"
 
41
#import "WebKitSystemInterface.h"
 
42
#import "WebNSDataExtras.h"
 
43
#import "WebNSDictionaryExtras.h"
 
44
#import "WebNSObjectExtras.h"
 
45
#import "WebNSURLExtras.h"
 
46
#import "WebNSURLRequestExtras.h"
 
47
#import "WebNSViewExtras.h"
 
48
#import "WebNetscapePluginPackage.h"
 
49
#import "WebNetscapePluginStream.h"
 
50
#import "WebNullPluginView.h"
 
51
#import "WebPreferences.h"
 
52
#import "WebViewInternal.h"
 
53
#import <Carbon/Carbon.h>
 
54
#import <JavaScriptCore/Assertions.h>
 
55
#import <JavaScriptCore/JSLock.h>
 
56
#import <JavaScriptCore/npruntime_impl.h>
 
57
#import <WebCore/Document.h>
 
58
#import <WebCore/Element.h>
 
59
#import <WebCore/Frame.h> 
 
60
#import <WebCore/FrameLoader.h> 
 
61
#import <WebCore/FrameTree.h> 
 
62
#import <WebCore/Page.h> 
 
63
#import <WebCore/WebCoreObjCExtras.h>
 
64
#import <WebKit/DOMPrivate.h>
 
65
#import <WebKit/WebUIDelegate.h>
 
66
#import <objc/objc-runtime.h>
 
67
 
 
68
using namespace WebCore;
 
69
 
 
70
// Send null events 50 times a second when active, so plug-ins like Flash get high frame rates.
 
71
#define NullEventIntervalActive         0.02
 
72
#define NullEventIntervalNotActive      0.25
 
73
 
 
74
#define LoginWindowDidSwitchFromUserNotification    @"WebLoginWindowDidSwitchFromUserNotification"
 
75
#define LoginWindowDidSwitchToUserNotification      @"WebLoginWindowDidSwitchToUserNotification"
 
76
 
 
77
@interface WebBaseNetscapePluginView (Internal)
 
78
- (void)_viewHasMoved;
 
79
- (NPError)_createPlugin;
 
80
- (void)_destroyPlugin;
 
81
- (NSBitmapImageRep *)_printedPluginBitmap;
 
82
- (BOOL)_createAGLContextIfNeeded;
 
83
- (BOOL)_createWindowedAGLContext;
 
84
- (BOOL)_createWindowlessAGLContext;
 
85
- (CGLContextObj)_cglContext;
 
86
- (BOOL)_getAGLOffscreenBuffer:(GLvoid **)outBuffer width:(GLsizei *)outWidth height:(GLsizei *)outHeight;
 
87
- (void)_destroyAGLContext;
 
88
- (void)_reshapeAGLWindow;
 
89
- (void)_hideAGLWindow;
 
90
- (NSImage *)_aglOffscreenImageForDrawingInRect:(NSRect)drawingInRect;
 
91
- (void)_redeliverStream;
 
92
@end
 
93
 
 
94
static WebBaseNetscapePluginView *currentPluginView = nil;
 
95
 
 
96
typedef struct OpaquePortState* PortState;
 
97
 
 
98
#ifndef NP_NO_QUICKDRAW
 
99
 
 
100
// QuickDraw is not available in 64-bit
 
101
 
 
102
typedef struct {
 
103
    GrafPtr oldPort;
 
104
    GDHandle oldDevice;
 
105
    Point oldOrigin;
 
106
    RgnHandle oldClipRegion;
 
107
    RgnHandle oldVisibleRegion;
 
108
    RgnHandle clipRegion;
 
109
    BOOL forUpdate;
 
110
} PortState_QD;
 
111
 
 
112
#endif /* NP_NO_QUICKDRAW */
 
113
 
 
114
typedef struct {
 
115
    CGContextRef context;
 
116
} PortState_CG;
 
117
 
 
118
typedef struct {
 
119
    AGLContext oldContext;
 
120
} PortState_GL;
 
121
 
 
122
@interface WebPluginRequest : NSObject
 
123
{
 
124
    NSURLRequest *_request;
 
125
    NSString *_frameName;
 
126
    void *_notifyData;
 
127
    BOOL _didStartFromUserGesture;
 
128
    BOOL _sendNotification;
 
129
}
 
130
 
 
131
- (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture;
 
132
 
 
133
- (NSURLRequest *)request;
 
134
- (NSString *)frameName;
 
135
- (void *)notifyData;
 
136
- (BOOL)isCurrentEventUserGesture;
 
137
- (BOOL)sendNotification;
 
138
 
 
139
@end
 
140
 
 
141
@interface NSData (WebPluginDataExtras)
 
142
- (BOOL)_web_startsWithBlankLine;
 
143
- (NSInteger)_web_locationAfterFirstBlankLine;
 
144
@end
 
145
 
 
146
static OSStatus TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *pluginView);
 
147
 
 
148
@interface WebBaseNetscapePluginView (ForwardDeclarations)
 
149
- (void)setWindowIfNecessary;
 
150
- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification;
 
151
@end
 
152
 
 
153
@implementation WebBaseNetscapePluginView
 
154
 
 
155
+ (void)initialize
 
156
{
 
157
#ifndef BUILDING_ON_TIGER
 
158
    WebCoreObjCFinalizeOnMainThread(self);
 
159
#endif
 
160
    WKSendUserChangeNotifications();
 
161
}
 
162
 
 
163
#pragma mark EVENTS
 
164
 
 
165
+ (void)getCarbonEvent:(EventRecord *)carbonEvent
 
166
{
 
167
    carbonEvent->what = nullEvent;
 
168
    carbonEvent->message = 0;
 
169
    carbonEvent->when = TickCount();
 
170
    
 
171
    GetGlobalMouse(&carbonEvent->where);
 
172
    carbonEvent->where.h = static_cast<short>(carbonEvent->where.h * HIGetScaleFactor());
 
173
    carbonEvent->where.v = static_cast<short>(carbonEvent->where.v * HIGetScaleFactor());
 
174
    carbonEvent->modifiers = GetCurrentKeyModifiers();
 
175
    if (!Button())
 
176
        carbonEvent->modifiers |= btnState;
 
177
}
 
178
 
 
179
- (void)getCarbonEvent:(EventRecord *)carbonEvent
 
180
{
 
181
    [[self class] getCarbonEvent:carbonEvent];
 
182
}
 
183
 
 
184
- (EventModifiers)modifiersForEvent:(NSEvent *)event
 
185
{
 
186
    EventModifiers modifiers;
 
187
    unsigned int modifierFlags = [event modifierFlags];
 
188
    NSEventType eventType = [event type];
 
189
    
 
190
    modifiers = 0;
 
191
    
 
192
    if (eventType != NSLeftMouseDown && eventType != NSRightMouseDown)
 
193
        modifiers |= btnState;
 
194
    
 
195
    if (modifierFlags & NSCommandKeyMask)
 
196
        modifiers |= cmdKey;
 
197
    
 
198
    if (modifierFlags & NSShiftKeyMask)
 
199
        modifiers |= shiftKey;
 
200
 
 
201
    if (modifierFlags & NSAlphaShiftKeyMask)
 
202
        modifiers |= alphaLock;
 
203
 
 
204
    if (modifierFlags & NSAlternateKeyMask)
 
205
        modifiers |= optionKey;
 
206
 
 
207
    if (modifierFlags & NSControlKeyMask || eventType == NSRightMouseDown)
 
208
        modifiers |= controlKey;
 
209
    
 
210
    return modifiers;
 
211
}
 
212
 
 
213
- (void)getCarbonEvent:(EventRecord *)carbonEvent withEvent:(NSEvent *)cocoaEvent
 
214
{
 
215
    if (WKConvertNSEventToCarbonEvent(carbonEvent, cocoaEvent)) {
 
216
        carbonEvent->where.h = static_cast<short>(carbonEvent->where.h * HIGetScaleFactor());
 
217
        carbonEvent->where.v = static_cast<short>(carbonEvent->where.v * HIGetScaleFactor());
 
218
        return;
 
219
    }
 
220
    
 
221
    NSPoint where = [[cocoaEvent window] convertBaseToScreen:[cocoaEvent locationInWindow]];
 
222
        
 
223
    carbonEvent->what = nullEvent;
 
224
    carbonEvent->message = 0;
 
225
    carbonEvent->when = (UInt32)([cocoaEvent timestamp] * 60); // seconds to ticks
 
226
    carbonEvent->where.h = (short)where.x;
 
227
    carbonEvent->where.v = (short)(NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - where.y);
 
228
    carbonEvent->modifiers = [self modifiersForEvent:cocoaEvent];
 
229
}
 
230
 
 
231
- (BOOL)superviewsHaveSuperviews
 
232
{
 
233
    NSView *contentView = [[self window] contentView];
 
234
    NSView *view;
 
235
    for (view = self; view != nil; view = [view superview]) { 
 
236
        if (view == contentView) {
 
237
            return YES;
 
238
        }
 
239
    }
 
240
    return NO;
 
241
}
 
242
 
 
243
#ifndef NP_NO_QUICKDRAW
 
244
 
 
245
// The WindowRef created by -[NSWindow windowRef] has a QuickDraw GrafPort that covers 
 
246
// the entire window frame (or structure region to use the Carbon term) rather then just the window content.
 
247
// We can remove this when <rdar://problem/4201099> is fixed.
 
248
- (void)fixWindowPort
 
249
{
 
250
    ASSERT(drawingModel == NPDrawingModelQuickDraw);
 
251
    
 
252
    NSWindow *currentWindow = [self currentWindow];
 
253
    if ([currentWindow isKindOfClass:objc_getClass("NSCarbonWindow")])
 
254
        return;
 
255
    
 
256
    float windowHeight = [currentWindow frame].size.height;
 
257
    NSView *contentView = [currentWindow contentView];
 
258
    NSRect contentRect = [contentView convertRect:[contentView frame] toView:nil]; // convert to window-relative coordinates
 
259
    
 
260
    CGrafPtr oldPort;
 
261
    GetPort(&oldPort);    
 
262
    SetPort(GetWindowPort((WindowRef)[currentWindow windowRef]));
 
263
    
 
264
    MovePortTo(static_cast<short>(contentRect.origin.x), /* Flip Y */ static_cast<short>(windowHeight - NSMaxY(contentRect)));
 
265
    PortSize(static_cast<short>(contentRect.size.width), static_cast<short>(contentRect.size.height));
 
266
    
 
267
    SetPort(oldPort);
 
268
}
 
269
 
 
270
static UInt32 getQDPixelFormatForBitmapContext(CGContextRef context)
 
271
{
 
272
    UInt32 byteOrder = CGBitmapContextGetBitmapInfo(context) & kCGBitmapByteOrderMask;
 
273
    if (byteOrder == kCGBitmapByteOrderDefault)
 
274
        switch (CGBitmapContextGetBitsPerPixel(context)) {
 
275
            case 16:
 
276
                byteOrder = kCGBitmapByteOrder16Host;
 
277
                break;
 
278
            case 32:
 
279
                byteOrder = kCGBitmapByteOrder32Host;
 
280
                break;
 
281
        }
 
282
    switch (byteOrder) {
 
283
        case kCGBitmapByteOrder16Little:
 
284
            return k16LE555PixelFormat;
 
285
        case kCGBitmapByteOrder32Little:
 
286
            return k32BGRAPixelFormat;
 
287
        case kCGBitmapByteOrder16Big:
 
288
            return k16BE555PixelFormat;
 
289
        case kCGBitmapByteOrder32Big:
 
290
            return k32ARGBPixelFormat;
 
291
    }
 
292
    ASSERT_NOT_REACHED();
 
293
    return 0;
 
294
}
 
295
 
 
296
static inline void getNPRect(const CGRect& cgr, NPRect& npr)
 
297
{
 
298
    npr.top = static_cast<uint16>(cgr.origin.y);
 
299
    npr.left = static_cast<uint16>(cgr.origin.x);
 
300
    npr.bottom = static_cast<uint16>(CGRectGetMaxY(cgr));
 
301
    npr.right = static_cast<uint16>(CGRectGetMaxX(cgr));
 
302
}
 
303
 
 
304
#endif
 
305
 
 
306
static inline void getNPRect(const NSRect& nr, NPRect& npr)
 
307
{
 
308
    npr.top = static_cast<uint16>(nr.origin.y);
 
309
    npr.left = static_cast<uint16>(nr.origin.x);
 
310
    npr.bottom = static_cast<uint16>(NSMaxY(nr));
 
311
    npr.right = static_cast<uint16>(NSMaxX(nr));
 
312
}
 
313
 
 
314
- (NSRect)visibleRect
 
315
{
 
316
    // WebCore may impose an additional clip (via CSS overflow or clip properties).  Fetch
 
317
    // that clip now.    
 
318
    return NSIntersectionRect([self convertRect:[element _windowClipRect] fromView:nil], [super visibleRect]);
 
319
}
 
320
 
 
321
- (PortState)saveAndSetNewPortStateForUpdate:(BOOL)forUpdate
 
322
{
 
323
    ASSERT([self currentWindow] != nil);
 
324
 
 
325
#ifndef NP_NO_QUICKDRAW
 
326
    // If drawing with QuickDraw, fix the window port so that it has the same bounds as the NSWindow's
 
327
    // content view.  This makes it easier to convert between AppKit view and QuickDraw port coordinates.
 
328
    if (drawingModel == NPDrawingModelQuickDraw)
 
329
        [self fixWindowPort];
 
330
#endif
 
331
 
 
332
    WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
 
333
    ASSERT(windowRef);
 
334
    
 
335
    // Use AppKit to convert view coordinates to NSWindow coordinates.
 
336
    NSRect boundsInWindow = [self convertRect:[self bounds] toView:nil];
 
337
    NSRect visibleRectInWindow = [self convertRect:[self visibleRect] toView:nil];
 
338
    
 
339
    // Flip Y to convert NSWindow coordinates to top-left-based window coordinates.
 
340
    float borderViewHeight = [[self currentWindow] frame].size.height;
 
341
    boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow);
 
342
    visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow);
 
343
    
 
344
#ifndef NP_NO_QUICKDRAW
 
345
    // Look at the Carbon port to convert top-left-based window coordinates into top-left-based content coordinates.
 
346
    if (drawingModel == NPDrawingModelQuickDraw) {
 
347
        ::Rect portBounds;
 
348
        CGrafPtr port = GetWindowPort(windowRef);
 
349
        GetPortBounds(port, &portBounds);
 
350
 
 
351
        PixMap *pix = *GetPortPixMap(port);
 
352
        boundsInWindow.origin.x += pix->bounds.left - portBounds.left;
 
353
        boundsInWindow.origin.y += pix->bounds.top - portBounds.top;
 
354
        visibleRectInWindow.origin.x += pix->bounds.left - portBounds.left;
 
355
        visibleRectInWindow.origin.y += pix->bounds.top - portBounds.top;
 
356
    }
 
357
#endif
 
358
    
 
359
    window.x = (int32)boundsInWindow.origin.x; 
 
360
    window.y = (int32)boundsInWindow.origin.y;
 
361
    window.width = static_cast<uint32>(NSWidth(boundsInWindow));
 
362
    window.height = static_cast<uint32>(NSHeight(boundsInWindow));
 
363
    
 
364
    // "Clip-out" the plug-in when:
 
365
    // 1) it's not really in a window or off-screen or has no height or width.
 
366
    // 2) window.x is a "big negative number" which is how WebCore expresses off-screen widgets.
 
367
    // 3) the window is miniaturized or the app is hidden
 
368
    // 4) we're inside of viewWillMoveToWindow: with a nil window. In this case, superviews may already have nil 
 
369
    // superviews and nil windows and results from convertRect:toView: are incorrect.
 
370
    NSWindow *realWindow = [self window];
 
371
    if (window.width <= 0 || window.height <= 0 || window.x < -100000
 
372
            || realWindow == nil || [realWindow isMiniaturized]
 
373
            || [NSApp isHidden]
 
374
            || ![self superviewsHaveSuperviews]
 
375
            || [self isHiddenOrHasHiddenAncestor]) {
 
376
 
 
377
        // The following code tries to give plug-ins the same size they will eventually have.
 
378
        // The specifiedWidth and specifiedHeight variables are used to predict the size that
 
379
        // WebCore will eventually resize us to.
 
380
 
 
381
        // The QuickTime plug-in has problems if you give it a width or height of 0.
 
382
        // Since other plug-ins also might have the same sort of trouble, we make sure
 
383
        // to always give plug-ins a size other than 0,0.
 
384
 
 
385
        if (window.width <= 0)
 
386
            window.width = specifiedWidth > 0 ? specifiedWidth : 100;
 
387
        if (window.height <= 0)
 
388
            window.height = specifiedHeight > 0 ? specifiedHeight : 100;
 
389
 
 
390
        window.clipRect.bottom = window.clipRect.top;
 
391
        window.clipRect.left = window.clipRect.right;
 
392
    } else {
 
393
        getNPRect(visibleRectInWindow, window.clipRect);
 
394
    }
 
395
    
 
396
    // Save the port state, set up the port for entry into the plugin
 
397
    PortState portState;
 
398
    switch (drawingModel) {
 
399
#ifndef NP_NO_QUICKDRAW
 
400
        case NPDrawingModelQuickDraw: {
 
401
            // Set up NS_Port.
 
402
            ::Rect portBounds;
 
403
            CGrafPtr port = GetWindowPort(windowRef);
 
404
            GetPortBounds(port, &portBounds);
 
405
            nPort.qdPort.port = port;
 
406
            nPort.qdPort.portx = (int32)-boundsInWindow.origin.x;
 
407
            nPort.qdPort.porty = (int32)-boundsInWindow.origin.y;
 
408
            window.window = &nPort;
 
409
 
 
410
            PortState_QD *qdPortState = (PortState_QD*)malloc(sizeof(PortState_QD));
 
411
            portState = (PortState)qdPortState;
 
412
            
 
413
            GetGWorld(&qdPortState->oldPort, &qdPortState->oldDevice);    
 
414
 
 
415
            qdPortState->oldOrigin.h = portBounds.left;
 
416
            qdPortState->oldOrigin.v = portBounds.top;
 
417
 
 
418
            qdPortState->oldClipRegion = NewRgn();
 
419
            GetPortClipRegion(port, qdPortState->oldClipRegion);
 
420
            
 
421
            qdPortState->oldVisibleRegion = NewRgn();
 
422
            GetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
 
423
            
 
424
            RgnHandle clipRegion = NewRgn();
 
425
            qdPortState->clipRegion = clipRegion;
 
426
 
 
427
            CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
 
428
            if (currentContext && WKCGContextIsBitmapContext(currentContext)) {
 
429
                // We use WKCGContextIsBitmapContext here, because if we just called CGBitmapContextGetData
 
430
                // on any context, we'd log to the console every time. But even if WKCGContextIsBitmapContext
 
431
                // returns true, it still might not be a context we need to create a GWorld for; for example
 
432
                // transparency layers will return true, but return 0 for CGBitmapContextGetData.
 
433
                void* offscreenData = CGBitmapContextGetData(currentContext);
 
434
                if (offscreenData) {
 
435
                    // If the current context is an offscreen bitmap, then create a GWorld for it.
 
436
                    ::Rect offscreenBounds;
 
437
                    offscreenBounds.top = 0;
 
438
                    offscreenBounds.left = 0;
 
439
                    offscreenBounds.right = CGBitmapContextGetWidth(currentContext);
 
440
                    offscreenBounds.bottom = CGBitmapContextGetHeight(currentContext);
 
441
                    GWorldPtr newOffscreenGWorld;
 
442
                    QDErr err = NewGWorldFromPtr(&newOffscreenGWorld,
 
443
                        getQDPixelFormatForBitmapContext(currentContext), &offscreenBounds, 0, 0, 0,
 
444
                        static_cast<char*>(offscreenData), CGBitmapContextGetBytesPerRow(currentContext));
 
445
                    ASSERT(newOffscreenGWorld && !err);
 
446
                    if (!err) {
 
447
                        if (offscreenGWorld)
 
448
                            DisposeGWorld(offscreenGWorld);
 
449
                        offscreenGWorld = newOffscreenGWorld;
 
450
 
 
451
                        SetGWorld(offscreenGWorld, NULL);
 
452
 
 
453
                        port = offscreenGWorld;
 
454
 
 
455
                        nPort.qdPort.port = port;
 
456
                        boundsInWindow = [self bounds];
 
457
                        
 
458
                        // Generate a QD origin based on the current affine transform for currentContext.
 
459
                        CGAffineTransform offscreenMatrix = CGContextGetCTM(currentContext);
 
460
                        CGPoint origin = {0,0};
 
461
                        CGPoint axisFlip = {1,1};
 
462
                        origin = CGPointApplyAffineTransform(origin, offscreenMatrix);
 
463
                        axisFlip = CGPointApplyAffineTransform(axisFlip, offscreenMatrix);
 
464
                        
 
465
                        // Quartz bitmaps have origins at the bottom left, but the axes may be inverted, so handle that.
 
466
                        origin.x = offscreenBounds.left - origin.x * (axisFlip.x - origin.x);
 
467
                        origin.y = offscreenBounds.bottom + origin.y * (axisFlip.y - origin.y);
 
468
                        
 
469
                        nPort.qdPort.portx = static_cast<int32>(-boundsInWindow.origin.x + origin.x);
 
470
                        nPort.qdPort.porty = static_cast<int32>(-boundsInWindow.origin.y - origin.y);
 
471
                        window.x = 0;
 
472
                        window.y = 0;
 
473
                        window.window = &nPort;
 
474
 
 
475
                        // Use the clip bounds from the context instead of the bounds we created
 
476
                        // from the window above.
 
477
                        getNPRect(CGContextGetClipBoundingBox(currentContext), window.clipRect);
 
478
                    }
 
479
                }
 
480
            }
 
481
 
 
482
            MacSetRectRgn(clipRegion,
 
483
                window.clipRect.left + nPort.qdPort.portx, window.clipRect.top + nPort.qdPort.porty,
 
484
                window.clipRect.right + nPort.qdPort.portx, window.clipRect.bottom + nPort.qdPort.porty);
 
485
            
 
486
            // Clip to dirty region so plug-in does not draw over already-drawn regions of the window that are
 
487
            // not going to be redrawn this update.  This forces plug-ins to play nice with z-index ordering.
 
488
            if (forUpdate) {
 
489
                RgnHandle viewClipRegion = NewRgn();
 
490
                
 
491
                // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
 
492
                // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
 
493
                // knows about the true set of dirty rects.
 
494
                NSView *opaqueAncestor = [self opaqueAncestor];
 
495
                const NSRect *dirtyRects;
 
496
                NSInteger dirtyRectCount, dirtyRectIndex;
 
497
                [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount];
 
498
 
 
499
                for (dirtyRectIndex = 0; dirtyRectIndex < dirtyRectCount; dirtyRectIndex++) {
 
500
                    NSRect dirtyRect = [self convertRect:dirtyRects[dirtyRectIndex] fromView:opaqueAncestor];
 
501
                    if (!NSEqualSizes(dirtyRect.size, NSZeroSize)) {
 
502
                        // Create a region for this dirty rect
 
503
                        RgnHandle dirtyRectRegion = NewRgn();
 
504
                        SetRectRgn(dirtyRectRegion, static_cast<short>(NSMinX(dirtyRect)), static_cast<short>(NSMinY(dirtyRect)), static_cast<short>(NSMaxX(dirtyRect)), static_cast<short>(NSMaxY(dirtyRect)));
 
505
                        
 
506
                        // Union this dirty rect with the rest of the dirty rects
 
507
                        UnionRgn(viewClipRegion, dirtyRectRegion, viewClipRegion);
 
508
                        DisposeRgn(dirtyRectRegion);
 
509
                    }
 
510
                }
 
511
            
 
512
                // Intersect the dirty region with the clip region, so that we only draw over dirty parts
 
513
                SectRgn(clipRegion, viewClipRegion, clipRegion);
 
514
                DisposeRgn(viewClipRegion);
 
515
            }
 
516
 
 
517
            // Switch to the port and set it up.
 
518
            SetPort(port);
 
519
            PenNormal();
 
520
            ForeColor(blackColor);
 
521
            BackColor(whiteColor);
 
522
            SetOrigin(nPort.qdPort.portx, nPort.qdPort.porty);
 
523
            SetPortClipRegion(nPort.qdPort.port, clipRegion);
 
524
 
 
525
            if (forUpdate) {
 
526
                // AppKit may have tried to help us by doing a BeginUpdate.
 
527
                // But the invalid region at that level didn't include AppKit's notion of what was not valid.
 
528
                // We reset the port's visible region to counteract what BeginUpdate did.
 
529
                SetPortVisibleRegion(nPort.qdPort.port, clipRegion);
 
530
                InvalWindowRgn(windowRef, clipRegion);
 
531
            }
 
532
            
 
533
            qdPortState->forUpdate = forUpdate;
 
534
            break;
 
535
        }
 
536
#endif /* NP_NO_QUICKDRAW */
 
537
 
 
538
        case NPDrawingModelCoreGraphics: {            
 
539
            // A CoreGraphics plugin's window may only be set while the plugin view is being updated
 
540
            ASSERT(forUpdate && [NSView focusView] == self);
 
541
 
 
542
            CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
 
543
 
 
544
            PortState_CG *cgPortState = (PortState_CG *)malloc(sizeof(PortState_CG));
 
545
            portState = (PortState)cgPortState;
 
546
            cgPortState->context = context;
 
547
            
 
548
            // Update the plugin's window/context
 
549
            nPort.cgPort.window = windowRef;
 
550
            nPort.cgPort.context = context;
 
551
            window.window = &nPort.cgPort;
 
552
 
 
553
            // Save current graphics context's state; will be restored by -restorePortState:
 
554
            CGContextSaveGState(context);
 
555
            
 
556
            // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and
 
557
            // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView
 
558
            // knows about the true set of dirty rects.
 
559
            NSView *opaqueAncestor = [self opaqueAncestor];
 
560
            const NSRect *dirtyRects;
 
561
            NSInteger count;
 
562
            [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&count];
 
563
            Vector<CGRect, 16> convertedDirtyRects;
 
564
            convertedDirtyRects.resize(count);
 
565
            for (int i = 0; i < count; ++i)
 
566
                reinterpret_cast<NSRect&>(convertedDirtyRects[i]) = [self convertRect:dirtyRects[i] fromView:opaqueAncestor];
 
567
            CGContextClipToRects(context, convertedDirtyRects.data(), count);
 
568
 
 
569
            break;
 
570
        }
 
571
 
 
572
        case NPDrawingModelOpenGL: {
 
573
            // An OpenGL plugin's window may only be set while the plugin view is being updated
 
574
            ASSERT(forUpdate && [NSView focusView] == self);
 
575
 
 
576
            // Clear the "current" window and context -- they will be assigned below (if all goes well)
 
577
            nPort.aglPort.window = NULL;
 
578
            nPort.aglPort.context = NULL;
 
579
            
 
580
            // Create AGL context if needed
 
581
            if (![self _createAGLContextIfNeeded]) {
 
582
                LOG_ERROR("Could not create AGL context");
 
583
                return NULL;
 
584
            }
 
585
            
 
586
            // Update the plugin's window/context
 
587
            nPort.aglPort.window = windowRef;
 
588
            nPort.aglPort.context = [self _cglContext];
 
589
            window.window = &nPort.aglPort;
 
590
            
 
591
            // Save/set current AGL context
 
592
            PortState_GL *glPortState = (PortState_GL *)malloc(sizeof(PortState_GL));
 
593
            portState = (PortState)glPortState;
 
594
            glPortState->oldContext = aglGetCurrentContext();
 
595
            aglSetCurrentContext(aglContext);
 
596
            
 
597
            // Adjust viewport according to clip
 
598
            switch (window.type) {
 
599
                case NPWindowTypeWindow:
 
600
                    glViewport(static_cast<GLint>(NSMinX(boundsInWindow) - NSMinX(visibleRectInWindow)),
 
601
                        static_cast<GLint>(NSMaxY(visibleRectInWindow) - NSMaxY(boundsInWindow)),
 
602
                            window.width, window.height);
 
603
                    break;
 
604
                
 
605
                case NPWindowTypeDrawable: {
 
606
                    GLsizei width, height;
 
607
                    if ([self _getAGLOffscreenBuffer:NULL width:&width height:&height])
 
608
                        glViewport(0, 0, width, height);
 
609
                    break;
 
610
                }
 
611
                
 
612
                default:
 
613
                    ASSERT_NOT_REACHED();
 
614
                    break;
 
615
            }
 
616
            break;
 
617
        }
 
618
        
 
619
        default:
 
620
            ASSERT_NOT_REACHED();
 
621
            portState = NULL;
 
622
            break;
 
623
    }
 
624
    
 
625
    return portState;
 
626
}
 
627
 
 
628
- (PortState)saveAndSetNewPortState
 
629
{
 
630
    return [self saveAndSetNewPortStateForUpdate:NO];
 
631
}
 
632
 
 
633
- (void)restorePortState:(PortState)portState
 
634
{
 
635
    ASSERT([self currentWindow]);
 
636
    ASSERT(portState);
 
637
    
 
638
    switch (drawingModel) {
 
639
#ifndef NP_NO_QUICKDRAW
 
640
        case NPDrawingModelQuickDraw: {
 
641
            PortState_QD *qdPortState = (PortState_QD *)portState;
 
642
            WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
 
643
            CGrafPtr port = GetWindowPort(windowRef);
 
644
 
 
645
            SetPort(port);
 
646
 
 
647
            if (qdPortState->forUpdate)
 
648
                ValidWindowRgn(windowRef, qdPortState->clipRegion);
 
649
 
 
650
            SetOrigin(qdPortState->oldOrigin.h, qdPortState->oldOrigin.v);
 
651
 
 
652
            SetPortClipRegion(port, qdPortState->oldClipRegion);
 
653
            if (qdPortState->forUpdate)
 
654
                SetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
 
655
 
 
656
            DisposeRgn(qdPortState->oldClipRegion);
 
657
            DisposeRgn(qdPortState->oldVisibleRegion);
 
658
            DisposeRgn(qdPortState->clipRegion);
 
659
 
 
660
            SetGWorld(qdPortState->oldPort, qdPortState->oldDevice);
 
661
            break;
 
662
        }
 
663
#endif /* NP_NO_QUICKDRAW */
 
664
        
 
665
        case NPDrawingModelCoreGraphics:
 
666
            ASSERT([NSView focusView] == self);
 
667
            ASSERT(((PortState_CG *)portState)->context == nPort.cgPort.context);
 
668
            CGContextRestoreGState(nPort.cgPort.context);
 
669
            break;
 
670
        
 
671
        case NPDrawingModelOpenGL:
 
672
            aglSetCurrentContext(((PortState_GL *)portState)->oldContext);
 
673
            break;
 
674
        
 
675
        default:
 
676
            ASSERT_NOT_REACHED();
 
677
            break;
 
678
    }
 
679
}
 
680
 
 
681
- (BOOL)sendEvent:(EventRecord *)event
 
682
{
 
683
    if (![self window])
 
684
        return NO;
 
685
    ASSERT(event);
 
686
   
 
687
    // If at any point the user clicks or presses a key from within a plugin, set the 
 
688
    // currentEventIsUserGesture flag to true. This is important to differentiate legitimate 
 
689
    // window.open() calls;  we still want to allow those.  See rdar://problem/4010765
 
690
    if (event->what == mouseDown || event->what == keyDown || event->what == mouseUp || event->what == autoKey)
 
691
        currentEventIsUserGesture = YES;
 
692
    
 
693
    suspendKeyUpEvents = NO;
 
694
    
 
695
    if (!isStarted)
 
696
        return NO;
 
697
 
 
698
    ASSERT(NPP_HandleEvent);
 
699
    
 
700
    // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
 
701
    // We probably don't want more general reentrancy protection; we are really
 
702
    // protecting only against this one case, which actually comes up when
 
703
    // you first install the SVG viewer plug-in.
 
704
    if (inSetWindow)
 
705
        return NO;
 
706
 
 
707
    Frame* frame = core([self webFrame]);
 
708
    if (!frame)
 
709
        return NO;
 
710
    Page* page = frame->page();
 
711
    if (!page)
 
712
        return NO;
 
713
 
 
714
    bool wasDeferring = page->defersLoading();
 
715
    if (!wasDeferring)
 
716
        page->setDefersLoading(true);
 
717
 
 
718
    // Can only send updateEvt to CoreGraphics and OpenGL plugins when actually drawing
 
719
    ASSERT((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || event->what != updateEvt || [NSView focusView] == self);
 
720
    
 
721
    BOOL updating = event->what == updateEvt;
 
722
    PortState portState;
 
723
    if ((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || event->what == updateEvt) {
 
724
        // In CoreGraphics or OpenGL mode, the port state only needs to be saved/set when redrawing the plug-in view.  The plug-in is not
 
725
        // allowed to draw at any other time.
 
726
        portState = [self saveAndSetNewPortStateForUpdate:updating];
 
727
        
 
728
        // We may have changed the window, so inform the plug-in.
 
729
        [self setWindowIfNecessary];
 
730
    } else
 
731
        portState = NULL;
 
732
    
 
733
#if !defined(NDEBUG) && !defined(NP_NO_QUICKDRAW)
 
734
    // Draw green to help debug.
 
735
    // If we see any green we know something's wrong.
 
736
    // Note that PaintRect() only works for QuickDraw plugins; otherwise the current QD port is undefined.
 
737
    if (drawingModel == NPDrawingModelQuickDraw && !isTransparent && event->what == updateEvt) {
 
738
        ForeColor(greenColor);
 
739
        const ::Rect bigRect = { -10000, -10000, 10000, 10000 };
 
740
        PaintRect(&bigRect);
 
741
        ForeColor(blackColor);
 
742
    }
 
743
#endif
 
744
    
 
745
    // Temporarily retain self in case the plug-in view is released while sending an event. 
 
746
    [[self retain] autorelease];
 
747
    
 
748
    BOOL acceptedEvent;
 
749
    [self willCallPlugInFunction];
 
750
    {
 
751
        KJS::JSLock::DropAllLocks dropAllLocks;
 
752
        acceptedEvent = NPP_HandleEvent(plugin, event);
 
753
    }
 
754
    [self didCallPlugInFunction];
 
755
    
 
756
    currentEventIsUserGesture = NO;
 
757
    
 
758
    if (portState) {
 
759
        if ([self currentWindow])
 
760
            [self restorePortState:portState];
 
761
        free(portState);
 
762
    }
 
763
 
 
764
    if (!wasDeferring)
 
765
        page->setDefersLoading(false);
 
766
            
 
767
    return acceptedEvent;
 
768
}
 
769
 
 
770
- (void)sendActivateEvent:(BOOL)activate
 
771
{
 
772
    EventRecord event;
 
773
    
 
774
    [self getCarbonEvent:&event];
 
775
    event.what = activateEvt;
 
776
    WindowRef windowRef = (WindowRef)[[self window] windowRef];
 
777
    event.message = (unsigned long)windowRef;
 
778
    if (activate) {
 
779
        event.modifiers |= activeFlag;
 
780
    }
 
781
    
 
782
    BOOL acceptedEvent;
 
783
    acceptedEvent = [self sendEvent:&event]; 
 
784
    
 
785
    LOG(PluginEvents, "NPP_HandleEvent(activateEvent): %d  isActive: %d", acceptedEvent, activate);
 
786
}
 
787
 
 
788
- (BOOL)sendUpdateEvent
 
789
{
 
790
    EventRecord event;
 
791
    
 
792
    [self getCarbonEvent:&event];
 
793
    event.what = updateEvt;
 
794
    WindowRef windowRef = (WindowRef)[[self window] windowRef];
 
795
    event.message = (unsigned long)windowRef;
 
796
 
 
797
    BOOL acceptedEvent = [self sendEvent:&event];
 
798
 
 
799
    LOG(PluginEvents, "NPP_HandleEvent(updateEvt): %d", acceptedEvent);
 
800
 
 
801
    return acceptedEvent;
 
802
}
 
803
 
 
804
-(void)sendNullEvent
 
805
{
 
806
    EventRecord event;
 
807
 
 
808
    [self getCarbonEvent:&event];
 
809
 
 
810
    // Plug-in should not react to cursor position when not active or when a menu is down.
 
811
    MenuTrackingData trackingData;
 
812
    OSStatus error = GetMenuTrackingData(NULL, &trackingData);
 
813
 
 
814
    // Plug-in should not react to cursor position when the actual window is not key.
 
815
    if (![[self window] isKeyWindow] || (error == noErr && trackingData.menu)) {
 
816
        // FIXME: Does passing a v and h of -1 really prevent it from reacting to the cursor position?
 
817
        event.where.v = -1;
 
818
        event.where.h = -1;
 
819
    }
 
820
 
 
821
    [self sendEvent:&event];
 
822
}
 
823
 
 
824
- (void)stopNullEvents
 
825
{
 
826
    [nullEventTimer invalidate];
 
827
    [nullEventTimer release];
 
828
    nullEventTimer = nil;
 
829
}
 
830
 
 
831
- (void)restartNullEvents
 
832
{
 
833
    ASSERT([self window]);
 
834
    
 
835
    if (nullEventTimer)
 
836
        [self stopNullEvents];
 
837
    
 
838
    if (!isStarted || [[self window] isMiniaturized])
 
839
        return;
 
840
 
 
841
    NSTimeInterval interval;
 
842
 
 
843
    // If the plugin is completely obscured (scrolled out of view, for example), then we will
 
844
    // send null events at a reduced rate.
 
845
    interval = !isCompletelyObscured ? NullEventIntervalActive : NullEventIntervalNotActive;    
 
846
    nullEventTimer = [[NSTimer scheduledTimerWithTimeInterval:interval
 
847
                                                       target:self
 
848
                                                     selector:@selector(sendNullEvent)
 
849
                                                     userInfo:nil
 
850
                                                      repeats:YES] retain];
 
851
}
 
852
 
 
853
- (BOOL)acceptsFirstResponder
 
854
{
 
855
    return YES;
 
856
}
 
857
 
 
858
- (void)installKeyEventHandler
 
859
{
 
860
    static const EventTypeSpec sTSMEvents[] =
 
861
    {
 
862
    { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
 
863
    };
 
864
    
 
865
    if (!keyEventHandler) {
 
866
        InstallEventHandler(GetWindowEventTarget((WindowRef)[[self window] windowRef]),
 
867
                            NewEventHandlerUPP(TSMEventHandler),
 
868
                            GetEventTypeCount(sTSMEvents),
 
869
                            sTSMEvents,
 
870
                            self,
 
871
                            &keyEventHandler);
 
872
    }
 
873
}
 
874
 
 
875
- (void)removeKeyEventHandler
 
876
{
 
877
    if (keyEventHandler) {
 
878
        RemoveEventHandler(keyEventHandler);
 
879
        keyEventHandler = NULL;
 
880
    }
 
881
}
 
882
 
 
883
- (void)setHasFocus:(BOOL)flag
 
884
{
 
885
    if (hasFocus != flag) {
 
886
        hasFocus = flag;
 
887
        EventRecord event;
 
888
        [self getCarbonEvent:&event];
 
889
        BOOL acceptedEvent;
 
890
        if (hasFocus) {
 
891
            event.what = getFocusEvent;
 
892
            acceptedEvent = [self sendEvent:&event]; 
 
893
            LOG(PluginEvents, "NPP_HandleEvent(getFocusEvent): %d", acceptedEvent);
 
894
            [self installKeyEventHandler];
 
895
        } else {
 
896
            event.what = loseFocusEvent;
 
897
            acceptedEvent = [self sendEvent:&event]; 
 
898
            LOG(PluginEvents, "NPP_HandleEvent(loseFocusEvent): %d", acceptedEvent);
 
899
            [self removeKeyEventHandler];
 
900
        }
 
901
    }
 
902
}
 
903
 
 
904
- (BOOL)becomeFirstResponder
 
905
{
 
906
    [self setHasFocus:YES];
 
907
    return YES;
 
908
}
 
909
 
 
910
- (BOOL)resignFirstResponder
 
911
{
 
912
    [self setHasFocus:NO];    
 
913
    return YES;
 
914
}
 
915
 
 
916
// AppKit doesn't call mouseDown or mouseUp on right-click. Simulate control-click
 
917
// mouseDown and mouseUp so plug-ins get the right-click event as they do in Carbon (3125743).
 
918
- (void)rightMouseDown:(NSEvent *)theEvent
 
919
{
 
920
    [self mouseDown:theEvent];
 
921
}
 
922
 
 
923
- (void)rightMouseUp:(NSEvent *)theEvent
 
924
{
 
925
    [self mouseUp:theEvent];
 
926
}
 
927
 
 
928
- (void)mouseDown:(NSEvent *)theEvent
 
929
{
 
930
    EventRecord event;
 
931
 
 
932
    [self getCarbonEvent:&event withEvent:theEvent];
 
933
    event.what = mouseDown;
 
934
 
 
935
    BOOL acceptedEvent;
 
936
    acceptedEvent = [self sendEvent:&event]; 
 
937
    
 
938
    LOG(PluginEvents, "NPP_HandleEvent(mouseDown): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
 
939
}
 
940
 
 
941
- (void)mouseUp:(NSEvent *)theEvent
 
942
{
 
943
    EventRecord event;
 
944
    
 
945
    [self getCarbonEvent:&event withEvent:theEvent];
 
946
    event.what = mouseUp;
 
947
 
 
948
    BOOL acceptedEvent;
 
949
    acceptedEvent = [self sendEvent:&event]; 
 
950
    
 
951
    LOG(PluginEvents, "NPP_HandleEvent(mouseUp): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
 
952
}
 
953
 
 
954
- (void)mouseEntered:(NSEvent *)theEvent
 
955
{
 
956
    EventRecord event;
 
957
    
 
958
    [self getCarbonEvent:&event withEvent:theEvent];
 
959
    event.what = adjustCursorEvent;
 
960
 
 
961
    BOOL acceptedEvent;
 
962
    acceptedEvent = [self sendEvent:&event]; 
 
963
    
 
964
    LOG(PluginEvents, "NPP_HandleEvent(mouseEntered): %d", acceptedEvent);
 
965
}
 
966
 
 
967
- (void)mouseExited:(NSEvent *)theEvent
 
968
{
 
969
    EventRecord event;
 
970
        
 
971
    [self getCarbonEvent:&event withEvent:theEvent];
 
972
    event.what = adjustCursorEvent;
 
973
 
 
974
    BOOL acceptedEvent;
 
975
    acceptedEvent = [self sendEvent:&event]; 
 
976
    
 
977
    LOG(PluginEvents, "NPP_HandleEvent(mouseExited): %d", acceptedEvent);
 
978
    
 
979
    // Set cursor back to arrow cursor.
 
980
    [[NSCursor arrowCursor] set];
 
981
}
 
982
 
 
983
- (void)mouseDragged:(NSEvent *)theEvent
 
984
{
 
985
    // Do nothing so that other responders don't respond to the drag that initiated in this view.
 
986
}
 
987
 
 
988
- (UInt32)keyMessageForEvent:(NSEvent *)event
 
989
{
 
990
    NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())];
 
991
    if (!data) {
 
992
        return 0;
 
993
    }
 
994
    UInt8 characterCode;
 
995
    [data getBytes:&characterCode length:1];
 
996
    UInt16 keyCode = [event keyCode];
 
997
    return keyCode << 8 | characterCode;
 
998
}
 
999
 
 
1000
- (void)keyUp:(NSEvent *)theEvent
 
1001
{
 
1002
    WKSendKeyEventToTSM(theEvent);
 
1003
    
 
1004
    // TSM won't send keyUp events so we have to send them ourselves.
 
1005
    // Only send keyUp events after we receive the TSM callback because this is what plug-in expect from OS 9.
 
1006
    if (!suspendKeyUpEvents) {
 
1007
        EventRecord event;
 
1008
        
 
1009
        [self getCarbonEvent:&event withEvent:theEvent];
 
1010
        event.what = keyUp;
 
1011
        
 
1012
        if (event.message == 0) {
 
1013
            event.message = [self keyMessageForEvent:theEvent];
 
1014
        }
 
1015
        
 
1016
        [self sendEvent:&event];
 
1017
    }
 
1018
}
 
1019
 
 
1020
- (void)keyDown:(NSEvent *)theEvent
 
1021
{
 
1022
    suspendKeyUpEvents = YES;
 
1023
    WKSendKeyEventToTSM(theEvent);
 
1024
}
 
1025
 
 
1026
static OSStatus TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *pluginView)
 
1027
{    
 
1028
    EventRef rawKeyEventRef;
 
1029
    OSStatus status = GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(EventRef), NULL, &rawKeyEventRef);
 
1030
    if (status != noErr) {
 
1031
        LOG_ERROR("GetEventParameter failed with error: %d", status);
 
1032
        return noErr;
 
1033
    }
 
1034
    
 
1035
    // Two-pass read to allocate/extract Mac charCodes
 
1036
    ByteCount numBytes;    
 
1037
    status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL);
 
1038
    if (status != noErr) {
 
1039
        LOG_ERROR("GetEventParameter failed with error: %d", status);
 
1040
        return noErr;
 
1041
    }
 
1042
    char *buffer = (char *)malloc(numBytes);
 
1043
    status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, numBytes, NULL, buffer);
 
1044
    if (status != noErr) {
 
1045
        LOG_ERROR("GetEventParameter failed with error: %d", status);
 
1046
        free(buffer);
 
1047
        return noErr;
 
1048
    }
 
1049
    
 
1050
    EventRef cloneEvent = CopyEvent(rawKeyEventRef);
 
1051
    unsigned i;
 
1052
    for (i = 0; i < numBytes; i++) {
 
1053
        status = SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes, typeChar, 1 /* one char code */, &buffer[i]);
 
1054
        if (status != noErr) {
 
1055
            LOG_ERROR("SetEventParameter failed with error: %d", status);
 
1056
            free(buffer);
 
1057
            return noErr;
 
1058
        }
 
1059
        
 
1060
        EventRecord eventRec;
 
1061
        if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
 
1062
            BOOL acceptedEvent;
 
1063
            acceptedEvent = [(WebBaseNetscapePluginView *)pluginView sendEvent:&eventRec];
 
1064
            
 
1065
            LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu",
 
1066
                acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask));
 
1067
            
 
1068
            // We originally thought that if the plug-in didn't accept this event,
 
1069
            // we should pass it along so that keyboard scrolling, for example, will work.
 
1070
            // In practice, this is not a good idea, because plug-ins tend to eat the event but return false.
 
1071
            // MacIE handles each key event twice because of this, but we will emulate the other browsers instead.
 
1072
        }
 
1073
    }
 
1074
    ReleaseEvent(cloneEvent);
 
1075
    
 
1076
    free(buffer);
 
1077
 
 
1078
    return noErr;
 
1079
}
 
1080
 
 
1081
// Fake up command-modified events so cut, copy, paste and select all menus work.
 
1082
- (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character
 
1083
{
 
1084
    EventRecord event;
 
1085
    [self getCarbonEvent:&event];
 
1086
    event.what = keyDown;
 
1087
    event.modifiers |= cmdKey;
 
1088
    event.message = keyCode << 8 | character;
 
1089
    [self sendEvent:&event];
 
1090
}
 
1091
 
 
1092
- (void)cut:(id)sender
 
1093
{
 
1094
    [self sendModifierEventWithKeyCode:7 character:'x'];
 
1095
}
 
1096
 
 
1097
- (void)copy:(id)sender
 
1098
{
 
1099
    [self sendModifierEventWithKeyCode:8 character:'c'];
 
1100
}
 
1101
 
 
1102
- (void)paste:(id)sender
 
1103
{
 
1104
    [self sendModifierEventWithKeyCode:9 character:'v'];
 
1105
}
 
1106
 
 
1107
- (void)selectAll:(id)sender
 
1108
{
 
1109
    [self sendModifierEventWithKeyCode:0 character:'a'];
 
1110
}
 
1111
 
 
1112
#pragma mark WEB_NETSCAPE_PLUGIN
 
1113
 
 
1114
- (BOOL)isNewWindowEqualToOldWindow
 
1115
{
 
1116
    if (window.x != lastSetWindow.x)
 
1117
        return NO;
 
1118
    if (window.y != lastSetWindow.y)
 
1119
        return NO;
 
1120
    if (window.width != lastSetWindow.width)
 
1121
        return NO;
 
1122
    if (window.height != lastSetWindow.height)
 
1123
        return NO;
 
1124
    if (window.clipRect.top != lastSetWindow.clipRect.top)
 
1125
        return NO;
 
1126
    if (window.clipRect.left != lastSetWindow.clipRect.left)
 
1127
        return NO;
 
1128
    if (window.clipRect.bottom  != lastSetWindow.clipRect.bottom)
 
1129
        return NO;
 
1130
    if (window.clipRect.right != lastSetWindow.clipRect.right)
 
1131
        return NO;
 
1132
    if (window.type != lastSetWindow.type)
 
1133
        return NO;
 
1134
    
 
1135
    switch (drawingModel) {
 
1136
#ifndef NP_NO_QUICKDRAW
 
1137
        case NPDrawingModelQuickDraw:
 
1138
            if (nPort.qdPort.portx != lastSetPort.qdPort.portx)
 
1139
                return NO;
 
1140
            if (nPort.qdPort.porty != lastSetPort.qdPort.porty)
 
1141
                return NO;
 
1142
            if (nPort.qdPort.port != lastSetPort.qdPort.port)
 
1143
                return NO;
 
1144
        break;
 
1145
#endif /* NP_NO_QUICKDRAW */
 
1146
            
 
1147
        case NPDrawingModelCoreGraphics:
 
1148
            if (nPort.cgPort.window != lastSetPort.cgPort.window)
 
1149
                return NO;
 
1150
            if (nPort.cgPort.context != lastSetPort.cgPort.context)
 
1151
                return NO;
 
1152
        break;
 
1153
            
 
1154
        case NPDrawingModelOpenGL:
 
1155
            if (nPort.aglPort.window != lastSetPort.aglPort.window)
 
1156
                return NO;
 
1157
            if (nPort.aglPort.context != lastSetPort.aglPort.context)
 
1158
                return NO;
 
1159
        break;
 
1160
        
 
1161
        default:
 
1162
            ASSERT_NOT_REACHED();
 
1163
        break;
 
1164
    }
 
1165
    
 
1166
    return YES;
 
1167
}
 
1168
 
 
1169
- (void)updateAndSetWindow
 
1170
{
 
1171
    if (drawingModel == NPDrawingModelCoreGraphics || drawingModel == NPDrawingModelOpenGL) {
 
1172
        // Can only update CoreGraphics and OpenGL plugins while redrawing the plugin view
 
1173
        [self setNeedsDisplay:YES];
 
1174
        return;
 
1175
    }
 
1176
    
 
1177
    // Can't update the plugin if it has not started (or has been stopped)
 
1178
    if (!isStarted)
 
1179
        return;
 
1180
        
 
1181
    PortState portState = [self saveAndSetNewPortState];
 
1182
    if (portState) {
 
1183
        [self setWindowIfNecessary];
 
1184
        [self restorePortState:portState];
 
1185
        free(portState);
 
1186
    }
 
1187
}
 
1188
 
 
1189
- (void)setWindowIfNecessary
 
1190
{
 
1191
    if (!isStarted) {
 
1192
        return;
 
1193
    }
 
1194
    
 
1195
    if (![self isNewWindowEqualToOldWindow]) {        
 
1196
        // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow.
 
1197
        // We probably don't want more general reentrancy protection; we are really
 
1198
        // protecting only against this one case, which actually comes up when
 
1199
        // you first install the SVG viewer plug-in.
 
1200
        NPError npErr;
 
1201
        ASSERT(!inSetWindow);
 
1202
        
 
1203
        inSetWindow = YES;
 
1204
        
 
1205
        // A CoreGraphics or OpenGL plugin's window may only be set while the plugin is being updated
 
1206
        ASSERT((drawingModel != NPDrawingModelCoreGraphics && drawingModel != NPDrawingModelOpenGL) || [NSView focusView] == self);
 
1207
        
 
1208
        [self willCallPlugInFunction];
 
1209
        {
 
1210
            KJS::JSLock::DropAllLocks dropAllLocks;
 
1211
            npErr = NPP_SetWindow(plugin, &window);
 
1212
        }
 
1213
        [self didCallPlugInFunction];
 
1214
        inSetWindow = NO;
 
1215
 
 
1216
#ifndef NDEBUG
 
1217
        switch (drawingModel) {
 
1218
#ifndef NP_NO_QUICKDRAW
 
1219
            case NPDrawingModelQuickDraw:
 
1220
                LOG(Plugins, "NPP_SetWindow (QuickDraw): %d, port=0x%08x, window.x:%d window.y:%d window.width:%d window.height:%d",
 
1221
                npErr, (int)nPort.qdPort.port, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
 
1222
            break;
 
1223
#endif /* NP_NO_QUICKDRAW */
 
1224
            
 
1225
            case NPDrawingModelCoreGraphics:
 
1226
                LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d",
 
1227
                npErr, nPort.cgPort.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
 
1228
            break;
 
1229
 
 
1230
            case NPDrawingModelOpenGL:
 
1231
                LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d",
 
1232
                npErr, nPort.aglPort.window, nPort.aglPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height);
 
1233
            break;
 
1234
            
 
1235
            default:
 
1236
                ASSERT_NOT_REACHED();
 
1237
            break;
 
1238
        }
 
1239
#endif /* !defined(NDEBUG) */
 
1240
        
 
1241
        lastSetWindow = window;
 
1242
        lastSetPort = nPort;
 
1243
    }
 
1244
}
 
1245
 
 
1246
- (void)removeTrackingRect
 
1247
{
 
1248
    if (trackingTag) {
 
1249
        [self removeTrackingRect:trackingTag];
 
1250
        trackingTag = 0;
 
1251
 
 
1252
        // Do the following after setting trackingTag to 0 so we don't re-enter.
 
1253
 
 
1254
        // Balance the retain in resetTrackingRect. Use autorelease in case we hold 
 
1255
        // the last reference to the window during tear-down, to avoid crashing AppKit. 
 
1256
        [[self window] autorelease];
 
1257
    }
 
1258
}
 
1259
 
 
1260
- (void)resetTrackingRect
 
1261
{
 
1262
    [self removeTrackingRect];
 
1263
    if (isStarted) {
 
1264
        // Retain the window so that removeTrackingRect can work after the window is closed.
 
1265
        [[self window] retain];
 
1266
        trackingTag = [self addTrackingRect:[self bounds] owner:self userData:nil assumeInside:NO];
 
1267
    }
 
1268
}
 
1269
 
 
1270
+ (void)setCurrentPluginView:(WebBaseNetscapePluginView *)view
 
1271
{
 
1272
    currentPluginView = view;
 
1273
}
 
1274
 
 
1275
+ (WebBaseNetscapePluginView *)currentPluginView
 
1276
{
 
1277
    return currentPluginView;
 
1278
}
 
1279
 
 
1280
- (BOOL)canStart
 
1281
{
 
1282
    return YES;
 
1283
}
 
1284
 
 
1285
- (void)didStart
 
1286
{
 
1287
    if (_loadManually) {
 
1288
        [self _redeliverStream];
 
1289
        return;
 
1290
    }
 
1291
    
 
1292
    // If the OBJECT/EMBED tag has no SRC, the URL is passed to us as "".
 
1293
    // Check for this and don't start a load in this case.
 
1294
    if (sourceURL != nil && ![sourceURL _web_isEmpty]) {
 
1295
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:sourceURL];
 
1296
        [request _web_setHTTPReferrer:core([self webFrame])->loader()->outgoingReferrer()];
 
1297
        [self loadRequest:request inTarget:nil withNotifyData:nil sendNotification:NO];
 
1298
    } 
 
1299
}
 
1300
 
 
1301
- (void)addWindowObservers
 
1302
{
 
1303
    ASSERT([self window]);
 
1304
 
 
1305
    NSWindow *theWindow = [self window];
 
1306
    
 
1307
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
 
1308
    [notificationCenter addObserver:self selector:@selector(windowWillClose:) 
 
1309
                               name:NSWindowWillCloseNotification object:theWindow]; 
 
1310
    [notificationCenter addObserver:self selector:@selector(windowBecameKey:)
 
1311
                               name:NSWindowDidBecomeKeyNotification object:theWindow];
 
1312
    [notificationCenter addObserver:self selector:@selector(windowResignedKey:)
 
1313
                               name:NSWindowDidResignKeyNotification object:theWindow];
 
1314
    [notificationCenter addObserver:self selector:@selector(windowDidMiniaturize:)
 
1315
                               name:NSWindowDidMiniaturizeNotification object:theWindow];
 
1316
    [notificationCenter addObserver:self selector:@selector(windowDidDeminiaturize:)
 
1317
                               name:NSWindowDidDeminiaturizeNotification object:theWindow];
 
1318
    
 
1319
    [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchFromUser:)
 
1320
                               name:LoginWindowDidSwitchFromUserNotification object:nil];
 
1321
    [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchToUser:)
 
1322
                               name:LoginWindowDidSwitchToUserNotification object:nil];
 
1323
}
 
1324
 
 
1325
- (void)removeWindowObservers
 
1326
{
 
1327
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
 
1328
    [notificationCenter removeObserver:self name:NSWindowWillCloseNotification        object:nil]; 
 
1329
    [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification     object:nil];
 
1330
    [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification     object:nil];
 
1331
    [notificationCenter removeObserver:self name:NSWindowDidMiniaturizeNotification   object:nil];
 
1332
    [notificationCenter removeObserver:self name:NSWindowDidDeminiaturizeNotification object:nil];
 
1333
    [notificationCenter removeObserver:self name:LoginWindowDidSwitchFromUserNotification   object:nil];
 
1334
    [notificationCenter removeObserver:self name:LoginWindowDidSwitchToUserNotification     object:nil];
 
1335
}
 
1336
 
 
1337
- (BOOL)start
 
1338
{
 
1339
    ASSERT([self currentWindow]);
 
1340
    
 
1341
    if (isStarted)
 
1342
        return YES;
 
1343
 
 
1344
    if (![self canStart])
 
1345
        return NO;
 
1346
    
 
1347
    ASSERT([self webView]);
 
1348
    
 
1349
    if (![[[self webView] preferences] arePlugInsEnabled])
 
1350
        return NO;
 
1351
 
 
1352
    // Open the plug-in package so it remains loaded while our plugin uses it
 
1353
    [pluginPackage open];
 
1354
    
 
1355
    // Initialize drawingModel to an invalid value so that we can detect when the plugin does not specify a drawingModel
 
1356
    drawingModel = (NPDrawingModel)-1;
 
1357
    
 
1358
    // Plug-ins are "windowed" by default.  On MacOS, windowed plug-ins share the same window and graphics port as the main
 
1359
    // browser window.  Windowless plug-ins are rendered off-screen, then copied into the main browser window.
 
1360
    window.type = NPWindowTypeWindow;
 
1361
    
 
1362
    NPError npErr = [self _createPlugin];
 
1363
    if (npErr != NPERR_NO_ERROR) {
 
1364
        LOG_ERROR("NPP_New failed with error: %d", npErr);
 
1365
        [self _destroyPlugin];
 
1366
        [pluginPackage close];
 
1367
        return NO;
 
1368
    }
 
1369
    
 
1370
    if (drawingModel == (NPDrawingModel)-1) {
 
1371
#ifndef NP_NO_QUICKDRAW
 
1372
        // Default to QuickDraw if the plugin did not specify a drawing model.
 
1373
        drawingModel = NPDrawingModelQuickDraw;
 
1374
#else
 
1375
        // QuickDraw is not available, so we can't default to it.  We could default to CoreGraphics instead, but
 
1376
        // if the plugin did not specify the CoreGraphics drawing model then it must be one of the old QuickDraw
 
1377
        // plugins.  Thus, the plugin is unsupported and should not be started.  Destroy it here and bail out.
 
1378
        LOG(Plugins, "Plugin only supports QuickDraw, but QuickDraw is unavailable: %@", pluginPackage);
 
1379
        [self _destroyPlugin];
 
1380
        [pluginPackage close];
 
1381
        return NO;
 
1382
#endif
 
1383
    }
 
1384
 
 
1385
    isStarted = YES;
 
1386
        
 
1387
    [self updateAndSetWindow];
 
1388
 
 
1389
    if ([self window]) {
 
1390
        [self addWindowObservers];
 
1391
        if ([[self window] isKeyWindow]) {
 
1392
            [self sendActivateEvent:YES];
 
1393
        }
 
1394
        [self restartNullEvents];
 
1395
    }
 
1396
 
 
1397
    [self resetTrackingRect];
 
1398
    
 
1399
    [self didStart];
 
1400
    
 
1401
    return YES;
 
1402
}
 
1403
 
 
1404
- (void)stop
 
1405
{
 
1406
    // If we're already calling a plug-in function, do not call NPP_Destroy().  The plug-in function we are calling
 
1407
    // may assume that its instance->pdata, or other memory freed by NPP_Destroy(), is valid and unchanged until said
 
1408
    // plugin-function returns.
 
1409
    // See <rdar://problem/4480737>.
 
1410
    if (pluginFunctionCallDepth > 0) {
 
1411
        shouldStopSoon = YES;
 
1412
        return;
 
1413
    }
 
1414
    
 
1415
    [self removeTrackingRect];
 
1416
 
 
1417
    if (!isStarted)
 
1418
        return;
 
1419
    
 
1420
    isStarted = NO;
 
1421
    // To stop active streams it's necessary to invoke makeObjectsPerformSelector on a copy 
 
1422
    // of streams. This is because calling -[WebNetscapePluginStream stop] also has the side effect
 
1423
    // of removing a stream from this collection.
 
1424
    NSArray *streamsCopy = [streams copy];
 
1425
    [streamsCopy makeObjectsPerformSelector:@selector(stop)];
 
1426
    [streamsCopy release];
 
1427
   
 
1428
    // Stop the null events
 
1429
    [self stopNullEvents];
 
1430
 
 
1431
    // Set cursor back to arrow cursor
 
1432
    [[NSCursor arrowCursor] set];
 
1433
    
 
1434
    // Stop notifications and callbacks.
 
1435
    [self removeWindowObservers];
 
1436
    [[pendingFrameLoads allKeys] makeObjectsPerformSelector:@selector(_setInternalLoadDelegate:) withObject:nil];
 
1437
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
 
1438
 
 
1439
    // Setting the window type to 0 ensures that NPP_SetWindow will be called if the plug-in is restarted.
 
1440
    lastSetWindow.type = (NPWindowType)0;
 
1441
    
 
1442
    [self _destroyPlugin];
 
1443
    [pluginPackage close];
 
1444
    
 
1445
    // We usually remove the key event handler in resignFirstResponder but it is possible that resignFirstResponder 
 
1446
    // may never get called so we can't completely rely on it.
 
1447
    [self removeKeyEventHandler];
 
1448
    
 
1449
    if (drawingModel == NPDrawingModelOpenGL)
 
1450
        [self _destroyAGLContext];
 
1451
}
 
1452
 
 
1453
- (BOOL)isStarted
 
1454
{
 
1455
    return isStarted;
 
1456
}
 
1457
 
 
1458
- (WebDataSource *)dataSource
 
1459
{
 
1460
    WebFrame *webFrame = kit(core(element)->document()->frame());
 
1461
    return [webFrame _dataSource];
 
1462
}
 
1463
 
 
1464
- (WebFrame *)webFrame
 
1465
{
 
1466
    return [[self dataSource] webFrame];
 
1467
}
 
1468
 
 
1469
- (WebView *)webView
 
1470
{
 
1471
    return [[self webFrame] webView];
 
1472
}
 
1473
 
 
1474
- (NSWindow *)currentWindow
 
1475
{
 
1476
    return [self window] ? [self window] : [[self webView] hostWindow];
 
1477
}
 
1478
 
 
1479
- (NPP)plugin
 
1480
{
 
1481
    return plugin;
 
1482
}
 
1483
 
 
1484
- (WebNetscapePluginPackage *)pluginPackage
 
1485
{
 
1486
    return pluginPackage;
 
1487
}
 
1488
 
 
1489
- (void)setPluginPackage:(WebNetscapePluginPackage *)thePluginPackage;
 
1490
{
 
1491
    [thePluginPackage retain];
 
1492
    [pluginPackage release];
 
1493
    pluginPackage = thePluginPackage;
 
1494
 
 
1495
    NPP_New =           [pluginPackage NPP_New];
 
1496
    NPP_Destroy =       [pluginPackage NPP_Destroy];
 
1497
    NPP_SetWindow =     [pluginPackage NPP_SetWindow];
 
1498
    NPP_NewStream =     [pluginPackage NPP_NewStream];
 
1499
    NPP_WriteReady =    [pluginPackage NPP_WriteReady];
 
1500
    NPP_Write =         [pluginPackage NPP_Write];
 
1501
    NPP_StreamAsFile =  [pluginPackage NPP_StreamAsFile];
 
1502
    NPP_DestroyStream = [pluginPackage NPP_DestroyStream];
 
1503
    NPP_HandleEvent =   [pluginPackage NPP_HandleEvent];
 
1504
    NPP_URLNotify =     [pluginPackage NPP_URLNotify];
 
1505
    NPP_GetValue =      [pluginPackage NPP_GetValue];
 
1506
    NPP_SetValue =      [pluginPackage NPP_SetValue];
 
1507
    NPP_Print =         [pluginPackage NPP_Print];
 
1508
}
 
1509
 
 
1510
- (void)setMIMEType:(NSString *)theMIMEType
 
1511
{
 
1512
    NSString *type = [theMIMEType copy];
 
1513
    [MIMEType release];
 
1514
    MIMEType = type;
 
1515
}
 
1516
 
 
1517
- (void)setBaseURL:(NSURL *)theBaseURL
 
1518
{
 
1519
    [theBaseURL retain];
 
1520
    [baseURL release];
 
1521
    baseURL = theBaseURL;
 
1522
}
 
1523
 
 
1524
- (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values;
 
1525
{
 
1526
    ASSERT([keys count] == [values count]);
 
1527
    
 
1528
    // Convert the attributes to 2 C string arrays.
 
1529
    // These arrays are passed to NPP_New, but the strings need to be
 
1530
    // modifiable and live the entire life of the plugin.
 
1531
 
 
1532
    // The Java plug-in requires the first argument to be the base URL
 
1533
    if ([MIMEType isEqualToString:@"application/x-java-applet"]) {
 
1534
        cAttributes = (char **)malloc(([keys count] + 1) * sizeof(char *));
 
1535
        cValues = (char **)malloc(([values count] + 1) * sizeof(char *));
 
1536
        cAttributes[0] = strdup("DOCBASE");
 
1537
        cValues[0] = strdup([baseURL _web_URLCString]);
 
1538
        argsCount++;
 
1539
    } else {
 
1540
        cAttributes = (char **)malloc([keys count] * sizeof(char *));
 
1541
        cValues = (char **)malloc([values count] * sizeof(char *));
 
1542
    }
 
1543
 
 
1544
    BOOL isWMP = [[[pluginPackage bundle] bundleIdentifier] isEqualToString:@"com.microsoft.WMP.defaultplugin"];
 
1545
    
 
1546
    unsigned i;
 
1547
    unsigned count = [keys count];
 
1548
    for (i = 0; i < count; i++) {
 
1549
        NSString *key = [keys objectAtIndex:i];
 
1550
        NSString *value = [values objectAtIndex:i];
 
1551
        if ([key _webkit_isCaseInsensitiveEqualToString:@"height"]) {
 
1552
            specifiedHeight = [value intValue];
 
1553
        } else if ([key _webkit_isCaseInsensitiveEqualToString:@"width"]) {
 
1554
            specifiedWidth = [value intValue];
 
1555
        }
 
1556
        // Avoid Window Media Player crash when these attributes are present.
 
1557
        if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) {
 
1558
            continue;
 
1559
        }
 
1560
        cAttributes[argsCount] = strdup([key UTF8String]);
 
1561
        cValues[argsCount] = strdup([value UTF8String]);
 
1562
        LOG(Plugins, "%@ = %@", key, value);
 
1563
        argsCount++;
 
1564
    }
 
1565
}
 
1566
 
 
1567
- (void)setMode:(int)theMode
 
1568
{
 
1569
    mode = theMode;
 
1570
}
 
1571
 
 
1572
#pragma mark NSVIEW
 
1573
 
 
1574
- (id)initWithFrame:(NSRect)frame
 
1575
      pluginPackage:(WebNetscapePluginPackage *)thePluginPackage
 
1576
                URL:(NSURL *)theURL
 
1577
            baseURL:(NSURL *)theBaseURL
 
1578
           MIMEType:(NSString *)MIME
 
1579
      attributeKeys:(NSArray *)keys
 
1580
    attributeValues:(NSArray *)values
 
1581
       loadManually:(BOOL)loadManually
 
1582
         DOMElement:(DOMElement *)anElement
 
1583
{
 
1584
    [super initWithFrame:frame];
 
1585
 
 
1586
    streams = [[NSMutableArray alloc] init];
 
1587
    pendingFrameLoads = [[NSMutableDictionary alloc] init];    
 
1588
    
 
1589
    // load the plug-in if it is not already loaded
 
1590
    if (![thePluginPackage load]) {
 
1591
        [self release];
 
1592
        return nil;
 
1593
    }
 
1594
    [self setPluginPackage:thePluginPackage];
 
1595
    
 
1596
    element = [anElement retain];
 
1597
    sourceURL = [theURL retain];
 
1598
    
 
1599
    [self setMIMEType:MIME];
 
1600
    [self setBaseURL:theBaseURL];
 
1601
    [self setAttributeKeys:keys andValues:values];
 
1602
    if (loadManually)
 
1603
        [self setMode:NP_FULL];
 
1604
    else
 
1605
        [self setMode:NP_EMBED];
 
1606
    
 
1607
    _loadManually = loadManually;
 
1608
    
 
1609
    return self;
 
1610
}
 
1611
 
 
1612
- (id)initWithFrame:(NSRect)frame
 
1613
{
 
1614
    ASSERT_NOT_REACHED();
 
1615
    return nil;
 
1616
}
 
1617
 
 
1618
- (void)fini
 
1619
{
 
1620
#ifndef NP_NO_QUICKDRAW
 
1621
    if (offscreenGWorld)
 
1622
        DisposeGWorld(offscreenGWorld);
 
1623
#endif
 
1624
 
 
1625
    unsigned i;
 
1626
    for (i = 0; i < argsCount; i++) {
 
1627
        free(cAttributes[i]);
 
1628
        free(cValues[i]);
 
1629
    }
 
1630
    free(cAttributes);
 
1631
    free(cValues);
 
1632
}
 
1633
 
 
1634
- (void)disconnectStream:(WebBaseNetscapePluginStream*)stream
 
1635
{
 
1636
    [streams removeObjectIdenticalTo:stream];    
 
1637
}
 
1638
 
 
1639
- (void)dealloc
 
1640
{
 
1641
    ASSERT(!isStarted);
 
1642
 
 
1643
    [sourceURL release];
 
1644
    [_manualStream release];
 
1645
    [_error release];
 
1646
    
 
1647
    [pluginPackage release];
 
1648
    [streams release];
 
1649
    [MIMEType release];
 
1650
    [baseURL release];
 
1651
    [pendingFrameLoads release];
 
1652
    [element release];
 
1653
    
 
1654
    ASSERT(!plugin);
 
1655
    ASSERT(!aglWindow);
 
1656
    ASSERT(!aglContext);
 
1657
 
 
1658
    [self fini];
 
1659
 
 
1660
    [super dealloc];
 
1661
}
 
1662
 
 
1663
- (void)finalize
 
1664
{
 
1665
    ASSERT_MAIN_THREAD();
 
1666
    ASSERT(!isStarted);
 
1667
 
 
1668
    [self fini];
 
1669
 
 
1670
    [super finalize];
 
1671
}
 
1672
 
 
1673
- (void)drawRect:(NSRect)rect
 
1674
{
 
1675
    if (!isStarted) {
 
1676
        return;
 
1677
    }
 
1678
    
 
1679
    if ([NSGraphicsContext currentContextDrawingToScreen])
 
1680
        [self sendUpdateEvent];
 
1681
    else {
 
1682
        NSBitmapImageRep *printedPluginBitmap = [self _printedPluginBitmap];
 
1683
        if (printedPluginBitmap) {
 
1684
            // Flip the bitmap before drawing because the QuickDraw port is flipped relative
 
1685
            // to this view.
 
1686
            CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
 
1687
            CGContextSaveGState(cgContext);
 
1688
            NSRect bounds = [self bounds];
 
1689
            CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
 
1690
            CGContextScaleCTM(cgContext, 1.0f, -1.0f);
 
1691
            [printedPluginBitmap drawInRect:bounds];
 
1692
            CGContextRestoreGState(cgContext);
 
1693
        }
 
1694
    }
 
1695
    
 
1696
    // If this is a windowless OpenGL plugin, blit its contents back into this view.  The plug-in just drew into the offscreen context.
 
1697
    if (drawingModel == NPDrawingModelOpenGL && window.type == NPWindowTypeDrawable) {
 
1698
        NSImage *aglOffscreenImage = [self _aglOffscreenImageForDrawingInRect:rect];
 
1699
        if (aglOffscreenImage) {
 
1700
            // Flip the context before drawing because the CGL context is flipped relative to this view.
 
1701
            CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
 
1702
            CGContextSaveGState(cgContext);
 
1703
            NSRect bounds = [self bounds];
 
1704
            CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds));
 
1705
            CGContextScaleCTM(cgContext, 1.0f, -1.0f);
 
1706
            
 
1707
            // Copy 'rect' from the offscreen buffer to this view (the flip above makes this sort of tricky)
 
1708
            NSRect flippedRect = rect;
 
1709
            flippedRect.origin.y = NSMaxY(bounds) - NSMaxY(flippedRect);
 
1710
            [aglOffscreenImage drawInRect:flippedRect fromRect:flippedRect operation:NSCompositeSourceOver fraction:1.0f];
 
1711
            CGContextRestoreGState(cgContext);
 
1712
        }
 
1713
    }
 
1714
}
 
1715
 
 
1716
- (BOOL)isFlipped
 
1717
{
 
1718
    return YES;
 
1719
}
 
1720
 
 
1721
- (void)renewGState
 
1722
{
 
1723
    [super renewGState];
 
1724
    
 
1725
    // -renewGState is called whenever the view's geometry changes.  It's a little hacky to override this method, but
 
1726
    // much safer than walking up the view hierarchy and observing frame/bounds changed notifications, since you don't
 
1727
    // have to track subsequent changes to the view hierarchy and add/remove notification observers.
 
1728
    // NSOpenGLView uses the exact same technique to reshape its OpenGL surface.
 
1729
    [self _viewHasMoved];
 
1730
}
 
1731
 
 
1732
#ifndef NP_NO_QUICKDRAW
 
1733
-(void)tellQuickTimeToChill
 
1734
{
 
1735
    ASSERT(drawingModel == NPDrawingModelQuickDraw);
 
1736
    
 
1737
    // Make a call to the secret QuickDraw API that makes QuickTime calm down.
 
1738
    WindowRef windowRef = (WindowRef)[[self window] windowRef];
 
1739
    if (!windowRef) {
 
1740
        return;
 
1741
    }
 
1742
    CGrafPtr port = GetWindowPort(windowRef);
 
1743
    ::Rect bounds;
 
1744
    GetPortBounds(port, &bounds);
 
1745
    WKCallDrawingNotification(port, &bounds);
 
1746
}
 
1747
#endif /* NP_NO_QUICKDRAW */
 
1748
 
 
1749
- (void)viewWillMoveToWindow:(NSWindow *)newWindow
 
1750
{
 
1751
#ifndef NP_NO_QUICKDRAW
 
1752
    if (drawingModel == NPDrawingModelQuickDraw)
 
1753
        [self tellQuickTimeToChill];
 
1754
#endif
 
1755
 
 
1756
    // We must remove the tracking rect before we move to the new window.
 
1757
    // Once we move to the new window, it will be too late.
 
1758
    [self removeTrackingRect];
 
1759
    [self removeWindowObservers];
 
1760
    
 
1761
    // Workaround for: <rdar://problem/3822871> resignFirstResponder is not sent to first responder view when it is removed from the window
 
1762
    [self setHasFocus:NO];
 
1763
 
 
1764
    if (!newWindow) {
 
1765
        // Hide the AGL child window
 
1766
        if (drawingModel == NPDrawingModelOpenGL)
 
1767
            [self _hideAGLWindow];
 
1768
        
 
1769
        if ([[self webView] hostWindow]) {
 
1770
            // View will be moved out of the actual window but it still has a host window.
 
1771
            [self stopNullEvents];
 
1772
        } else {
 
1773
            // View will have no associated windows.
 
1774
            [self stop];
 
1775
 
 
1776
            // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
 
1777
            // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
 
1778
            [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
 
1779
        }
 
1780
    }
 
1781
}
 
1782
 
 
1783
- (void)viewWillMoveToSuperview:(NSView *)newSuperview
 
1784
{
 
1785
    if (!newSuperview) {
 
1786
        // Stop the plug-in when it is removed from its superview.  It is not sufficient to do this in -viewWillMoveToWindow:nil, because
 
1787
        // the WebView might still has a hostWindow at that point, which prevents the plug-in from being destroyed.
 
1788
        // There is no need to start the plug-in when moving into a superview.  -viewDidMoveToWindow takes care of that.
 
1789
        [self stop];
 
1790
        
 
1791
        // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
 
1792
        // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
 
1793
        [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
 
1794
    }
 
1795
}
 
1796
 
 
1797
- (void)viewDidMoveToWindow
 
1798
{
 
1799
    [self resetTrackingRect];
 
1800
    
 
1801
    if ([self window]) {
 
1802
        // While in the view hierarchy, observe WebPreferencesChangedNotification so that we can start/stop depending
 
1803
        // on whether plugins are enabled.
 
1804
        [[NSNotificationCenter defaultCenter] addObserver:self
 
1805
                                              selector:@selector(preferencesHaveChanged:)
 
1806
                                              name:WebPreferencesChangedNotification
 
1807
                                              object:nil];
 
1808
 
 
1809
        // View moved to an actual window. Start it if not already started.
 
1810
        [self start];
 
1811
        [self restartNullEvents];
 
1812
        [self addWindowObservers];
 
1813
    } else if ([[self webView] hostWindow]) {
 
1814
        // View moved out of an actual window, but still has a host window.
 
1815
        // Call setWindow to explicitly "clip out" the plug-in from sight.
 
1816
        // FIXME: It would be nice to do this where we call stopNullEvents in viewWillMoveToWindow.
 
1817
        [self updateAndSetWindow];
 
1818
    }
 
1819
}
 
1820
 
 
1821
- (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
 
1822
{
 
1823
    if (!hostWindow && ![self window]) {
 
1824
        // View will have no associated windows.
 
1825
        [self stop];
 
1826
 
 
1827
        // Remove WebPreferencesChangedNotification observer -- we will observe once again when we move back into the window
 
1828
        [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
 
1829
    }
 
1830
}
 
1831
 
 
1832
- (void)viewDidMoveToHostWindow
 
1833
{
 
1834
    if ([[self webView] hostWindow]) {
 
1835
        // View now has an associated window. Start it if not already started.
 
1836
        [self start];
 
1837
    }
 
1838
}
 
1839
 
 
1840
#pragma mark NOTIFICATIONS
 
1841
 
 
1842
- (void)windowWillClose:(NSNotification *)notification 
 
1843
{
 
1844
    [self stop]; 
 
1845
 
1846
 
 
1847
- (void)windowBecameKey:(NSNotification *)notification
 
1848
{
 
1849
    [self sendActivateEvent:YES];
 
1850
    [self setNeedsDisplay:YES];
 
1851
    [self restartNullEvents];
 
1852
    SetUserFocusWindow((WindowRef)[[self window] windowRef]);
 
1853
}
 
1854
 
 
1855
- (void)windowResignedKey:(NSNotification *)notification
 
1856
{
 
1857
    [self sendActivateEvent:NO];
 
1858
    [self setNeedsDisplay:YES];
 
1859
    [self restartNullEvents];
 
1860
}
 
1861
 
 
1862
- (void)windowDidMiniaturize:(NSNotification *)notification
 
1863
{
 
1864
    [self stopNullEvents];
 
1865
}
 
1866
 
 
1867
- (void)windowDidDeminiaturize:(NSNotification *)notification
 
1868
{
 
1869
    [self restartNullEvents];
 
1870
}
 
1871
 
 
1872
- (void)loginWindowDidSwitchFromUser:(NSNotification *)notification
 
1873
{
 
1874
    [self stopNullEvents];
 
1875
}
 
1876
 
 
1877
-(void)loginWindowDidSwitchToUser:(NSNotification *)notification
 
1878
{
 
1879
    [self restartNullEvents];
 
1880
}
 
1881
 
 
1882
- (void)preferencesHaveChanged:(NSNotification *)notification
 
1883
{
 
1884
    WebPreferences *preferences = [[self webView] preferences];
 
1885
    BOOL arePlugInsEnabled = [preferences arePlugInsEnabled];
 
1886
    
 
1887
    if ([notification object] == preferences && isStarted != arePlugInsEnabled) {
 
1888
        if (arePlugInsEnabled) {
 
1889
            if ([self currentWindow]) {
 
1890
                [self start];
 
1891
            }
 
1892
        } else {
 
1893
            [self stop];
 
1894
            [self setNeedsDisplay:YES];
 
1895
        }
 
1896
    }
 
1897
}
 
1898
 
 
1899
- (NPObject *)createPluginScriptableObject
 
1900
{
 
1901
    if (!NPP_GetValue || ![self isStarted])
 
1902
        return NULL;
 
1903
        
 
1904
    NPObject *value = NULL;
 
1905
    NPError error;
 
1906
    [self willCallPlugInFunction];
 
1907
    {
 
1908
        KJS::JSLock::DropAllLocks dropAllLocks;
 
1909
        error = NPP_GetValue(plugin, NPPVpluginScriptableNPObject, &value);
 
1910
    }
 
1911
    [self didCallPlugInFunction];
 
1912
    if (error != NPERR_NO_ERROR)
 
1913
        return NULL;
 
1914
    
 
1915
    return value;
 
1916
}
 
1917
 
 
1918
- (void)willCallPlugInFunction
 
1919
{
 
1920
    ASSERT(plugin);
 
1921
 
 
1922
    // Could try to prevent infinite recursion here, but it's probably not worth the effort.
 
1923
    pluginFunctionCallDepth++;
 
1924
}
 
1925
 
 
1926
- (void)didCallPlugInFunction
 
1927
{
 
1928
    ASSERT(pluginFunctionCallDepth > 0);
 
1929
    pluginFunctionCallDepth--;
 
1930
    
 
1931
    // If -stop was called while we were calling into a plug-in function, and we're no longer
 
1932
    // inside a plug-in function, stop now.
 
1933
    if (pluginFunctionCallDepth == 0 && shouldStopSoon) {
 
1934
        shouldStopSoon = NO;
 
1935
        [self stop];
 
1936
    }
 
1937
}
 
1938
 
 
1939
-(void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
 
1940
{
 
1941
    ASSERT(_loadManually);
 
1942
    ASSERT(!_manualStream);
 
1943
    
 
1944
    _manualStream = [[WebNetscapePluginStream alloc] initWithFrameLoader:core([self webFrame])->loader()];
 
1945
}
 
1946
 
 
1947
- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
 
1948
{
 
1949
    ASSERT(_loadManually);
 
1950
    ASSERT(_manualStream);
 
1951
    
 
1952
    _dataLengthReceived += [data length];
 
1953
    
 
1954
    if (![self isStarted])
 
1955
        return;
 
1956
    
 
1957
    if ([_manualStream plugin] == NULL) {
 
1958
        [_manualStream setRequestURL:[[[self dataSource] request] URL]];
 
1959
        [_manualStream setPlugin:[self plugin]];
 
1960
        ASSERT([_manualStream plugin]);
 
1961
        [_manualStream startStreamWithResponse:[[self dataSource] response]];
 
1962
    }
 
1963
    
 
1964
    if ([_manualStream plugin])
 
1965
        [_manualStream receivedData:data];
 
1966
}
 
1967
 
 
1968
- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
 
1969
{
 
1970
    ASSERT(_loadManually);
 
1971
    
 
1972
    [error retain];
 
1973
    [_error release];
 
1974
    _error = error;
 
1975
    
 
1976
    if (![self isStarted]) {
 
1977
        return;
 
1978
    }
 
1979
    
 
1980
    [_manualStream destroyStreamWithError:error];
 
1981
}
 
1982
 
 
1983
- (void)pluginViewFinishedLoading:(NSView *)pluginView 
 
1984
{
 
1985
    ASSERT(_loadManually);
 
1986
    ASSERT(_manualStream);
 
1987
    
 
1988
    if ([self isStarted])
 
1989
        [_manualStream finishedLoadingWithData:[[self dataSource] data]];    
 
1990
}
 
1991
 
 
1992
@end
 
1993
 
 
1994
@implementation WebBaseNetscapePluginView (WebNPPCallbacks)
 
1995
 
 
1996
- (NSMutableURLRequest *)requestWithURLCString:(const char *)URLCString
 
1997
{
 
1998
    if (!URLCString)
 
1999
        return nil;
 
2000
    
 
2001
    CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, URLCString, kCFStringEncodingISOLatin1);
 
2002
    ASSERT(string); // All strings should be representable in ISO Latin 1
 
2003
    
 
2004
    NSString *URLString = [(NSString *)string _web_stringByStrippingReturnCharacters];
 
2005
    NSURL *URL = [NSURL _web_URLWithDataAsString:URLString relativeToURL:baseURL];
 
2006
    CFRelease(string);
 
2007
    if (!URL)
 
2008
        return nil;
 
2009
 
 
2010
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
 
2011
    Frame* frame = core([self webFrame]);
 
2012
    if (!frame)
 
2013
        return nil;
 
2014
    [request _web_setHTTPReferrer:frame->loader()->outgoingReferrer()];
 
2015
    return request;
 
2016
}
 
2017
 
 
2018
- (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest
 
2019
{
 
2020
    // FIXME: Is this isStarted check needed here? evaluateJavaScriptPluginRequest should not be called
 
2021
    // if we are stopped since this method is called after a delay and we call 
 
2022
    // cancelPreviousPerformRequestsWithTarget inside of stop.
 
2023
    if (!isStarted) {
 
2024
        return;
 
2025
    }
 
2026
    
 
2027
    NSURL *URL = [[JSPluginRequest request] URL];
 
2028
    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
 
2029
    ASSERT(JSString);
 
2030
    
 
2031
    NSString *result = [[[self webFrame] _bridge] stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]];
 
2032
    
 
2033
    // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop.
 
2034
    if (!isStarted) {
 
2035
        return;
 
2036
    }
 
2037
        
 
2038
    if ([JSPluginRequest frameName] != nil) {
 
2039
        // FIXME: If the result is a string, we probably want to put that string into the frame.
 
2040
        if ([JSPluginRequest sendNotification]) {
 
2041
            [self willCallPlugInFunction];
 
2042
            {
 
2043
                KJS::JSLock::DropAllLocks dropAllLocks;
 
2044
                NPP_URLNotify(plugin, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]);
 
2045
            }
 
2046
            [self didCallPlugInFunction];
 
2047
        }
 
2048
    } else if ([result length] > 0) {
 
2049
        // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does.
 
2050
        NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding];
 
2051
        WebBaseNetscapePluginStream *stream = [[WebBaseNetscapePluginStream alloc] initWithRequestURL:URL
 
2052
                                                                                               plugin:plugin
 
2053
                                                                                           notifyData:[JSPluginRequest notifyData]
 
2054
                                                                                     sendNotification:[JSPluginRequest sendNotification]];
 
2055
        [stream startStreamResponseURL:URL
 
2056
                 expectedContentLength:[JSData length]
 
2057
                      lastModifiedDate:nil
 
2058
                              MIMEType:@"text/plain"
 
2059
                               headers:nil];
 
2060
        [stream receivedData:JSData];
 
2061
        [stream finishedLoadingWithData:JSData];
 
2062
        [stream release];
 
2063
    }
 
2064
}
 
2065
 
 
2066
- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
 
2067
{
 
2068
    ASSERT(isStarted);
 
2069
    
 
2070
    WebPluginRequest *pluginRequest = [pendingFrameLoads objectForKey:webFrame];
 
2071
    ASSERT(pluginRequest != nil);
 
2072
    ASSERT([pluginRequest sendNotification]);
 
2073
        
 
2074
    [self willCallPlugInFunction];
 
2075
    {
 
2076
        KJS::JSLock::DropAllLocks dropAllLocks;
 
2077
        NPP_URLNotify(plugin, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]);
 
2078
    }
 
2079
    [self didCallPlugInFunction];
 
2080
    
 
2081
    [pendingFrameLoads removeObjectForKey:webFrame];
 
2082
    [webFrame _setInternalLoadDelegate:nil];
 
2083
}
 
2084
 
 
2085
- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
 
2086
{
 
2087
    NPReason reason = NPRES_DONE;
 
2088
    if (error != nil) {
 
2089
        reason = [WebBaseNetscapePluginStream reasonForError:error];
 
2090
    }    
 
2091
    [self webFrame:webFrame didFinishLoadWithReason:reason];
 
2092
}
 
2093
 
 
2094
- (void)loadPluginRequest:(WebPluginRequest *)pluginRequest
 
2095
{
 
2096
    NSURLRequest *request = [pluginRequest request];
 
2097
    NSString *frameName = [pluginRequest frameName];
 
2098
    WebFrame *frame = nil;
 
2099
    
 
2100
    NSURL *URL = [request URL];
 
2101
    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
 
2102
    
 
2103
    ASSERT(frameName || JSString);
 
2104
    
 
2105
    if (frameName) {
 
2106
        // FIXME - need to get rid of this window creation which
 
2107
        // bypasses normal targeted link handling
 
2108
        frame = [[self webFrame] findFrameNamed:frameName];
 
2109
    
 
2110
        if (frame == nil) {
 
2111
            WebView *newWebView = nil;
 
2112
            WebView *currentWebView = [self webView];
 
2113
            id wd = [currentWebView UIDelegate];
 
2114
            if ([wd respondsToSelector:@selector(webView:createWebViewWithRequest:)]) {
 
2115
                newWebView = [wd webView:currentWebView createWebViewWithRequest:nil];
 
2116
            } else {
 
2117
                newWebView = [[WebDefaultUIDelegate sharedUIDelegate] webView:currentWebView createWebViewWithRequest:nil];
 
2118
            }
 
2119
            
 
2120
            if (!newWebView) {
 
2121
                if ([pluginRequest sendNotification]) {
 
2122
                    [self willCallPlugInFunction];
 
2123
                    {
 
2124
                        KJS::JSLock::DropAllLocks dropAllLocks;
 
2125
                        NPP_URLNotify(plugin, [[[pluginRequest request] URL] _web_URLCString], NPERR_GENERIC_ERROR, [pluginRequest notifyData]);
 
2126
                    }
 
2127
                    [self didCallPlugInFunction];
 
2128
                }
 
2129
                return;
 
2130
            }
 
2131
            
 
2132
            frame = [newWebView mainFrame];
 
2133
            core(frame)->tree()->setName(frameName);
 
2134
            [[newWebView _UIDelegateForwarder] webViewShow:newWebView];
 
2135
        }
 
2136
    }
 
2137
 
 
2138
    if (JSString) {
 
2139
        ASSERT(frame == nil || [self webFrame] == frame);
 
2140
        [self evaluateJavaScriptPluginRequest:pluginRequest];
 
2141
    } else {
 
2142
        [frame loadRequest:request];
 
2143
        if ([pluginRequest sendNotification]) {
 
2144
            // Check if another plug-in view or even this view is waiting for the frame to load.
 
2145
            // If it is, tell it that the load was cancelled because it will be anyway.
 
2146
            WebBaseNetscapePluginView *view = [frame _internalLoadDelegate];
 
2147
            if (view != nil) {
 
2148
                ASSERT([view isKindOfClass:[WebBaseNetscapePluginView class]]);
 
2149
                [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK];
 
2150
            }
 
2151
            [pendingFrameLoads _webkit_setObject:pluginRequest forUncopiedKey:frame];
 
2152
            [frame _setInternalLoadDelegate:self];
 
2153
        }
 
2154
    }
 
2155
}
 
2156
 
 
2157
- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification
 
2158
{
 
2159
    NSURL *URL = [request URL];
 
2160
 
 
2161
    if (!URL) 
 
2162
        return NPERR_INVALID_URL;
 
2163
 
 
2164
    NSString *target = nil;
 
2165
    if (cTarget) {
 
2166
        // Find the frame given the target string.
 
2167
        target = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, cTarget, kCFStringEncodingWindowsLatin1);
 
2168
    }
 
2169
    WebFrame *frame = [self webFrame];
 
2170
 
 
2171
    // don't let a plugin start any loads if it is no longer part of a document that is being 
 
2172
    // displayed unless the loads are in the same frame as the plugin.
 
2173
    if ([[self dataSource] _documentLoader] != [[self webFrame] _frameLoader]->activeDocumentLoader() &&
 
2174
        (!cTarget || [frame findFrameNamed:target] != frame)) {
 
2175
        if (target)
 
2176
            CFRelease(target);
 
2177
        return NPERR_GENERIC_ERROR; 
 
2178
    }
 
2179
    
 
2180
    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
 
2181
    if (JSString != nil) {
 
2182
        if (![[[self webView] preferences] isJavaScriptEnabled]) {
 
2183
            // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does.
 
2184
            return NPERR_GENERIC_ERROR;
 
2185
        } else if (cTarget == NULL && mode == NP_FULL) {
 
2186
            // Don't allow a JavaScript request from a standalone plug-in that is self-targetted
 
2187
            // because this can cause the user to be redirected to a blank page (3424039).
 
2188
            return NPERR_INVALID_PARAM;
 
2189
        }
 
2190
    }
 
2191
        
 
2192
    if (cTarget || JSString) {
 
2193
        // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't
 
2194
        // want to potentially kill the plug-in inside of its URL request.
 
2195
        
 
2196
        if (JSString != nil && target != nil && [frame findFrameNamed:target] != frame) {
 
2197
            // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
 
2198
            CFRelease(target);
 
2199
            return NPERR_INVALID_PARAM;
 
2200
        }
 
2201
        
 
2202
        WebPluginRequest *pluginRequest = [[WebPluginRequest alloc] initWithRequest:request frameName:target notifyData:notifyData sendNotification:sendNotification didStartFromUserGesture:currentEventIsUserGesture];
 
2203
        [self performSelector:@selector(loadPluginRequest:) withObject:pluginRequest afterDelay:0];
 
2204
        [pluginRequest release];
 
2205
        if (target)
 
2206
            CFRelease(target);
 
2207
    } else {
 
2208
        WebNetscapePluginStream *stream = [[WebNetscapePluginStream alloc] initWithRequest:request 
 
2209
                                                                                    plugin:plugin 
 
2210
                                                                                notifyData:notifyData 
 
2211
                                                                          sendNotification:sendNotification];
 
2212
        if (!stream)
 
2213
            return NPERR_INVALID_URL;
 
2214
 
 
2215
        [streams addObject:stream];
 
2216
        [stream start];
 
2217
        [stream release];
 
2218
    }
 
2219
    
 
2220
    return NPERR_NO_ERROR;
 
2221
}
 
2222
 
 
2223
-(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData
 
2224
{
 
2225
    LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget);
 
2226
 
 
2227
    NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
 
2228
    return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES];
 
2229
}
 
2230
 
 
2231
-(NPError)getURL:(const char *)URLCString target:(const char *)cTarget
 
2232
{
 
2233
    LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget);
 
2234
 
 
2235
    NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
 
2236
    return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO];
 
2237
}
 
2238
 
 
2239
- (NPError)_postURL:(const char *)URLCString
 
2240
             target:(const char *)target
 
2241
                len:(UInt32)len
 
2242
                buf:(const char *)buf
 
2243
               file:(NPBool)file
 
2244
         notifyData:(void *)notifyData
 
2245
   sendNotification:(BOOL)sendNotification
 
2246
       allowHeaders:(BOOL)allowHeaders
 
2247
{
 
2248
    if (!URLCString || !len || !buf) {
 
2249
        return NPERR_INVALID_PARAM;
 
2250
    }
 
2251
    
 
2252
    NSData *postData = nil;
 
2253
 
 
2254
    if (file) {
 
2255
        // If we're posting a file, buf is either a file URL or a path to the file.
 
2256
        NSString *bufString = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingWindowsLatin1);
 
2257
        if (!bufString) {
 
2258
            return NPERR_INVALID_PARAM;
 
2259
        }
 
2260
        NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString];
 
2261
        NSString *path;
 
2262
        if ([fileURL isFileURL]) {
 
2263
            path = [fileURL path];
 
2264
        } else {
 
2265
            path = bufString;
 
2266
        }
 
2267
        postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]];
 
2268
        CFRelease(bufString);
 
2269
        if (!postData) {
 
2270
            return NPERR_FILE_NOT_FOUND;
 
2271
        }
 
2272
    } else {
 
2273
        postData = [NSData dataWithBytes:buf length:len];
 
2274
    }
 
2275
 
 
2276
    if ([postData length] == 0) {
 
2277
        return NPERR_INVALID_PARAM;
 
2278
    }
 
2279
 
 
2280
    NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
 
2281
    [request setHTTPMethod:@"POST"];
 
2282
    
 
2283
    if (allowHeaders) {
 
2284
        if ([postData _web_startsWithBlankLine]) {
 
2285
            postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)];
 
2286
        } else {
 
2287
            NSInteger location = [postData _web_locationAfterFirstBlankLine];
 
2288
            if (location != NSNotFound) {
 
2289
                // If the blank line is somewhere in the middle of postData, everything before is the header.
 
2290
                NSData *headerData = [postData subdataWithRange:NSMakeRange(0, location)];
 
2291
                NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields];
 
2292
                unsigned dataLength = [postData length] - location;
 
2293
 
 
2294
                // Sometimes plugins like to set Content-Length themselves when they post,
 
2295
                // but WebFoundation does not like that. So we will remove the header
 
2296
                // and instead truncate the data to the requested length.
 
2297
                NSString *contentLength = [header objectForKey:@"Content-Length"];
 
2298
 
 
2299
                if (contentLength != nil)
 
2300
                    dataLength = MIN((unsigned)[contentLength intValue], dataLength);
 
2301
                [header removeObjectForKey:@"Content-Length"];
 
2302
 
 
2303
                if ([header count] > 0) {
 
2304
                    [request setAllHTTPHeaderFields:header];
 
2305
                }
 
2306
                // Everything after the blank line is the actual content of the POST.
 
2307
                postData = [postData subdataWithRange:NSMakeRange(location, dataLength)];
 
2308
 
 
2309
            }
 
2310
        }
 
2311
        if ([postData length] == 0) {
 
2312
            return NPERR_INVALID_PARAM;
 
2313
        }
 
2314
    }
 
2315
 
 
2316
    // Plug-ins expect to receive uncached data when doing a POST (3347134).
 
2317
    [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
 
2318
    [request setHTTPBody:postData];
 
2319
    
 
2320
    return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification];
 
2321
}
 
2322
 
 
2323
- (NPError)postURLNotify:(const char *)URLCString
 
2324
                  target:(const char *)target
 
2325
                     len:(UInt32)len
 
2326
                     buf:(const char *)buf
 
2327
                    file:(NPBool)file
 
2328
              notifyData:(void *)notifyData
 
2329
{
 
2330
    LOG(Plugins, "NPN_PostURLNotify: %s", URLCString);
 
2331
    return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:notifyData sendNotification:YES allowHeaders:YES];
 
2332
}
 
2333
 
 
2334
-(NPError)postURL:(const char *)URLCString
 
2335
           target:(const char *)target
 
2336
              len:(UInt32)len
 
2337
              buf:(const char *)buf
 
2338
             file:(NPBool)file
 
2339
{
 
2340
    LOG(Plugins, "NPN_PostURL: %s", URLCString);        
 
2341
    // As documented, only allow headers to be specified via NPP_PostURL when using a file.
 
2342
    return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:NULL sendNotification:NO allowHeaders:file];
 
2343
}
 
2344
 
 
2345
-(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream
 
2346
{
 
2347
    LOG(Plugins, "NPN_NewStream");
 
2348
    return NPERR_GENERIC_ERROR;
 
2349
}
 
2350
 
 
2351
-(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer
 
2352
{
 
2353
    LOG(Plugins, "NPN_Write");
 
2354
    return NPERR_GENERIC_ERROR;
 
2355
}
 
2356
 
 
2357
-(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason
 
2358
{
 
2359
    LOG(Plugins, "NPN_DestroyStream");
 
2360
    // This function does a sanity check to ensure that the NPStream provided actually
 
2361
    // belongs to the plug-in that provided it, which fixes a crash in the DivX 
 
2362
    // plug-in: <rdar://problem/5093862> | http://bugs.webkit.org/show_bug.cgi?id=13203
 
2363
    if (!stream || [WebBaseNetscapePluginStream ownerForStream:stream] != plugin) {
 
2364
        LOG(Plugins, "Invalid NPStream passed to NPN_DestroyStream: %p", stream);
 
2365
        return NPERR_INVALID_INSTANCE_ERROR;
 
2366
    }
 
2367
    
 
2368
    WebBaseNetscapePluginStream *browserStream = static_cast<WebBaseNetscapePluginStream *>(stream->ndata);
 
2369
    [browserStream cancelLoadAndDestroyStreamWithError:[browserStream errorForReason:reason]];
 
2370
    
 
2371
    return NPERR_NO_ERROR;
 
2372
}
 
2373
 
 
2374
- (const char *)userAgent
 
2375
{
 
2376
    return [[[self webView] userAgentForURL:baseURL] UTF8String];
 
2377
}
 
2378
 
 
2379
-(void)status:(const char *)message
 
2380
{    
 
2381
    if (!message) {
 
2382
        LOG_ERROR("NPN_Status passed a NULL status message");
 
2383
        return;
 
2384
    }
 
2385
 
 
2386
    CFStringRef status = CFStringCreateWithCString(NULL, message, kCFStringEncodingUTF8);
 
2387
    if (!status) {
 
2388
        LOG_ERROR("NPN_Status: the message was not valid UTF-8");
 
2389
        return;
 
2390
    }
 
2391
    
 
2392
    LOG(Plugins, "NPN_Status: %@", status);
 
2393
    WebView *wv = [self webView];
 
2394
    [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status];
 
2395
    CFRelease(status);
 
2396
}
 
2397
 
 
2398
-(void)invalidateRect:(NPRect *)invalidRect
 
2399
{
 
2400
    LOG(Plugins, "NPN_InvalidateRect");
 
2401
    [self setNeedsDisplayInRect:NSMakeRect(invalidRect->left, invalidRect->top,
 
2402
        (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)];
 
2403
}
 
2404
 
 
2405
-(bool)isOpaque
 
2406
{
 
2407
    return YES;
 
2408
}
 
2409
 
 
2410
- (void)invalidateRegion:(NPRegion)invalidRegion
 
2411
{
 
2412
    LOG(Plugins, "NPN_InvalidateRegion");
 
2413
    NSRect invalidRect = NSZeroRect;
 
2414
    switch (drawingModel) {
 
2415
#ifndef NP_NO_QUICKDRAW
 
2416
        case NPDrawingModelQuickDraw:
 
2417
        {
 
2418
            ::Rect qdRect;
 
2419
            GetRegionBounds((NPQDRegion)invalidRegion, &qdRect);
 
2420
            invalidRect = NSMakeRect(qdRect.left, qdRect.top, qdRect.right - qdRect.left, qdRect.bottom - qdRect.top);
 
2421
        }
 
2422
        break;
 
2423
#endif /* NP_NO_QUICKDRAW */
 
2424
        
 
2425
        case NPDrawingModelCoreGraphics:
 
2426
        case NPDrawingModelOpenGL:
 
2427
        {
 
2428
            CGRect cgRect = CGPathGetBoundingBox((NPCGRegion)invalidRegion);
 
2429
            invalidRect = *(NSRect *)&cgRect;
 
2430
        }
 
2431
        break;
 
2432
    
 
2433
        default:
 
2434
            ASSERT_NOT_REACHED();
 
2435
        break;
 
2436
    }
 
2437
    
 
2438
    [self setNeedsDisplayInRect:invalidRect];
 
2439
}
 
2440
 
 
2441
-(void)forceRedraw
 
2442
{
 
2443
    LOG(Plugins, "forceRedraw");
 
2444
    [self setNeedsDisplay:YES];
 
2445
    [[self window] displayIfNeeded];
 
2446
}
 
2447
 
 
2448
- (NPError)getVariable:(NPNVariable)variable value:(void *)value
 
2449
{
 
2450
    switch (variable) {
 
2451
        case NPNVWindowNPObject:
 
2452
        {
 
2453
            Frame* frame = core([self webFrame]);
 
2454
            NPObject* windowScriptObject = frame ? frame->windowScriptNPObject() : 0;
 
2455
 
 
2456
            // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
 
2457
            if (windowScriptObject)
 
2458
                _NPN_RetainObject(windowScriptObject);
 
2459
            
 
2460
            void **v = (void **)value;
 
2461
            *v = windowScriptObject;
 
2462
 
 
2463
            return NPERR_NO_ERROR;
 
2464
        }
 
2465
 
 
2466
        case NPNVPluginElementNPObject:
 
2467
        {
 
2468
            if (!element)
 
2469
                return NPERR_GENERIC_ERROR;
 
2470
            
 
2471
            NPObject *plugInScriptObject = (NPObject *)[element _NPObject];
 
2472
 
 
2473
            // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
 
2474
            if (plugInScriptObject)
 
2475
                _NPN_RetainObject(plugInScriptObject);
 
2476
 
 
2477
            void **v = (void **)value;
 
2478
            *v = plugInScriptObject;
 
2479
 
 
2480
            return NPERR_NO_ERROR;
 
2481
        }
 
2482
        
 
2483
        case NPNVpluginDrawingModel:
 
2484
        {
 
2485
            *(NPDrawingModel *)value = drawingModel;
 
2486
            return NPERR_NO_ERROR;
 
2487
        }
 
2488
 
 
2489
#ifndef NP_NO_QUICKDRAW
 
2490
        case NPNVsupportsQuickDrawBool:
 
2491
        {
 
2492
            *(NPBool *)value = TRUE;
 
2493
            return NPERR_NO_ERROR;
 
2494
        }
 
2495
#endif /* NP_NO_QUICKDRAW */
 
2496
        
 
2497
        case NPNVsupportsCoreGraphicsBool:
 
2498
        {
 
2499
            *(NPBool *)value = TRUE;
 
2500
            return NPERR_NO_ERROR;
 
2501
        }
 
2502
 
 
2503
        case NPNVsupportsOpenGLBool:
 
2504
        {
 
2505
            *(NPBool *)value = TRUE;
 
2506
            return NPERR_NO_ERROR;
 
2507
        }
 
2508
        
 
2509
        default:
 
2510
            break;
 
2511
    }
 
2512
 
 
2513
    return NPERR_GENERIC_ERROR;
 
2514
}
 
2515
 
 
2516
- (NPError)setVariable:(NPPVariable)variable value:(void *)value
 
2517
{
 
2518
    switch (variable) {
 
2519
        case NPPVpluginWindowBool:
 
2520
        {
 
2521
            NPWindowType newWindowType = (value ? NPWindowTypeWindow : NPWindowTypeDrawable);
 
2522
 
 
2523
            // Redisplay if window type is changing (some drawing models can only have their windows set while updating).
 
2524
            if (newWindowType != window.type)
 
2525
                [self setNeedsDisplay:YES];
 
2526
            
 
2527
            window.type = newWindowType;
 
2528
        }
 
2529
        
 
2530
        case NPPVpluginTransparentBool:
 
2531
        {
 
2532
            BOOL newTransparent = (value != 0);
 
2533
            
 
2534
            // Redisplay if transparency is changing
 
2535
            if (isTransparent != newTransparent)
 
2536
                [self setNeedsDisplay:YES];
 
2537
            
 
2538
            isTransparent = newTransparent;
 
2539
            
 
2540
            return NPERR_NO_ERROR;
 
2541
        }
 
2542
        
 
2543
        case NPNVpluginDrawingModel:
 
2544
        {
 
2545
            // Can only set drawing model inside NPP_New()
 
2546
            if (self != [[self class] currentPluginView])
 
2547
                return NPERR_GENERIC_ERROR;
 
2548
            
 
2549
            // Check for valid, supported drawing model
 
2550
            NPDrawingModel newDrawingModel = (NPDrawingModel)(uintptr_t)value;
 
2551
            switch (newDrawingModel) {
 
2552
                // Supported drawing models:
 
2553
#ifndef NP_NO_QUICKDRAW
 
2554
                case NPDrawingModelQuickDraw:
 
2555
#endif
 
2556
                case NPDrawingModelCoreGraphics:
 
2557
                case NPDrawingModelOpenGL:
 
2558
                    drawingModel = newDrawingModel;
 
2559
                    return NPERR_NO_ERROR;
 
2560
                
 
2561
                // Unsupported (or unknown) drawing models:
 
2562
                default:
 
2563
                    LOG(Plugins, "Plugin %@ uses unsupported drawing model: %d", pluginPackage, drawingModel);
 
2564
                    return NPERR_GENERIC_ERROR;
 
2565
            }
 
2566
        }
 
2567
        
 
2568
        default:
 
2569
            return NPERR_GENERIC_ERROR;
 
2570
    }
 
2571
}
 
2572
 
 
2573
@end
 
2574
 
 
2575
@implementation WebPluginRequest
 
2576
 
 
2577
- (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture
 
2578
{
 
2579
    [super init];
 
2580
    _didStartFromUserGesture = currentEventIsUserGesture;
 
2581
    _request = [request retain];
 
2582
    _frameName = [frameName retain];
 
2583
    _notifyData = notifyData;
 
2584
    _sendNotification = sendNotification;
 
2585
    return self;
 
2586
}
 
2587
 
 
2588
- (void)dealloc
 
2589
{
 
2590
    [_request release];
 
2591
    [_frameName release];
 
2592
    [super dealloc];
 
2593
}
 
2594
 
 
2595
- (NSURLRequest *)request
 
2596
{
 
2597
    return _request;
 
2598
}
 
2599
 
 
2600
- (NSString *)frameName
 
2601
{
 
2602
    return _frameName;
 
2603
}
 
2604
 
 
2605
- (BOOL)isCurrentEventUserGesture
 
2606
{
 
2607
    return _didStartFromUserGesture;
 
2608
}
 
2609
 
 
2610
- (BOOL)sendNotification
 
2611
{
 
2612
    return _sendNotification;
 
2613
}
 
2614
 
 
2615
- (void *)notifyData
 
2616
{
 
2617
    return _notifyData;
 
2618
}
 
2619
 
 
2620
@end
 
2621
 
 
2622
@implementation WebBaseNetscapePluginView (Internal)
 
2623
 
 
2624
- (NPError)_createPlugin
 
2625
{
 
2626
    plugin = (NPP)calloc(1, sizeof(NPP_t));
 
2627
    plugin->ndata = self;
 
2628
 
 
2629
    ASSERT(NPP_New);
 
2630
 
 
2631
    // NPN_New(), which creates the plug-in instance, should never be called while calling a plug-in function for that instance.
 
2632
    ASSERT(pluginFunctionCallDepth == 0);
 
2633
 
 
2634
    [[self class] setCurrentPluginView:self];
 
2635
    NPError npErr = NPP_New((char *)[MIMEType cString], plugin, mode, argsCount, cAttributes, cValues, NULL);
 
2636
    [[self class] setCurrentPluginView:nil];
 
2637
    
 
2638
    LOG(Plugins, "NPP_New: %d", npErr);
 
2639
    return npErr;
 
2640
}
 
2641
 
 
2642
- (void)_destroyPlugin
 
2643
{
 
2644
    NPError npErr;
 
2645
    npErr = NPP_Destroy(plugin, NULL);
 
2646
    LOG(Plugins, "NPP_Destroy: %d", npErr);
 
2647
    
 
2648
    if (Frame* frame = core([self webFrame]))
 
2649
        frame->cleanupScriptObjectsForPlugin(self);
 
2650
        
 
2651
    free(plugin);
 
2652
    plugin = NULL;
 
2653
}
 
2654
 
 
2655
- (void)_viewHasMoved
 
2656
{
 
2657
    // All of the work this method does may safely be skipped if the view is not in a window.  When the view
 
2658
    // is moved back into a window, everything should be set up correctly.
 
2659
    if (![self window])
 
2660
        return;
 
2661
    
 
2662
    if (drawingModel == NPDrawingModelOpenGL)
 
2663
        [self _reshapeAGLWindow];
 
2664
 
 
2665
#ifndef NP_NO_QUICKDRAW
 
2666
    if (drawingModel == NPDrawingModelQuickDraw)
 
2667
        [self tellQuickTimeToChill];
 
2668
#endif
 
2669
    [self updateAndSetWindow];
 
2670
    [self resetTrackingRect];
 
2671
    
 
2672
    // Check to see if the plugin view is completely obscured (scrolled out of view, for example).
 
2673
    // For performance reasons, we send null events at a lower rate to plugins which are obscured.
 
2674
    BOOL oldIsObscured = isCompletelyObscured;
 
2675
    isCompletelyObscured = NSIsEmptyRect([self visibleRect]);
 
2676
    if (isCompletelyObscured != oldIsObscured)
 
2677
        [self restartNullEvents];
 
2678
}
 
2679
 
 
2680
- (NSBitmapImageRep *)_printedPluginBitmap
 
2681
{
 
2682
#ifdef NP_NO_QUICKDRAW
 
2683
    return nil;
 
2684
#else
 
2685
    // Cannot print plugins that do not implement NPP_Print
 
2686
    if (!NPP_Print)
 
2687
        return nil;
 
2688
 
 
2689
    // This NSBitmapImageRep will share its bitmap buffer with a GWorld that the plugin will draw into.
 
2690
    // The bitmap is created in 32-bits-per-pixel ARGB format, which is the default GWorld pixel format.
 
2691
    NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
 
2692
                                                         pixelsWide:window.width
 
2693
                                                         pixelsHigh:window.height
 
2694
                                                         bitsPerSample:8
 
2695
                                                         samplesPerPixel:4
 
2696
                                                         hasAlpha:YES
 
2697
                                                         isPlanar:NO
 
2698
                                                         colorSpaceName:NSDeviceRGBColorSpace
 
2699
                                                         bitmapFormat:NSAlphaFirstBitmapFormat
 
2700
                                                         bytesPerRow:0
 
2701
                                                         bitsPerPixel:0] autorelease];
 
2702
    ASSERT(bitmap);
 
2703
    
 
2704
    // Create a GWorld with the same underlying buffer into which the plugin can draw
 
2705
    ::Rect printGWorldBounds;
 
2706
    SetRect(&printGWorldBounds, 0, 0, window.width, window.height);
 
2707
    GWorldPtr printGWorld;
 
2708
    if (NewGWorldFromPtr(&printGWorld,
 
2709
                         k32ARGBPixelFormat,
 
2710
                         &printGWorldBounds,
 
2711
                         NULL,
 
2712
                         NULL,
 
2713
                         0,
 
2714
                         (Ptr)[bitmap bitmapData],
 
2715
                         [bitmap bytesPerRow]) != noErr) {
 
2716
        LOG_ERROR("Could not create GWorld for printing");
 
2717
        return nil;
 
2718
    }
 
2719
    
 
2720
    /// Create NPWindow for the GWorld
 
2721
    NPWindow printNPWindow;
 
2722
    printNPWindow.window = &printGWorld; // Normally this is an NP_Port, but when printing it is the actual CGrafPtr
 
2723
    printNPWindow.x = 0;
 
2724
    printNPWindow.y = 0;
 
2725
    printNPWindow.width = window.width;
 
2726
    printNPWindow.height = window.height;
 
2727
    printNPWindow.clipRect.top = 0;
 
2728
    printNPWindow.clipRect.left = 0;
 
2729
    printNPWindow.clipRect.right = window.width;
 
2730
    printNPWindow.clipRect.bottom = window.height;
 
2731
    printNPWindow.type = NPWindowTypeDrawable; // Offscreen graphics port as opposed to a proper window
 
2732
    
 
2733
    // Create embed-mode NPPrint
 
2734
    NPPrint npPrint;
 
2735
    npPrint.mode = NP_EMBED;
 
2736
    npPrint.print.embedPrint.window = printNPWindow;
 
2737
    npPrint.print.embedPrint.platformPrint = printGWorld;
 
2738
    
 
2739
    // Tell the plugin to print into the GWorld
 
2740
    [self willCallPlugInFunction];
 
2741
    {
 
2742
        KJS::JSLock::DropAllLocks dropAllLocks;
 
2743
        NPP_Print(plugin, &npPrint);
 
2744
    }
 
2745
    [self didCallPlugInFunction];
 
2746
 
 
2747
    // Don't need the GWorld anymore
 
2748
    DisposeGWorld(printGWorld);
 
2749
        
 
2750
    return bitmap;
 
2751
#endif
 
2752
}
 
2753
 
 
2754
- (BOOL)_createAGLContextIfNeeded
 
2755
{
 
2756
    ASSERT(drawingModel == NPDrawingModelOpenGL);
 
2757
 
 
2758
    // Do nothing (but indicate success) if the AGL context already exists
 
2759
    if (aglContext)
 
2760
        return YES;
 
2761
        
 
2762
    switch (window.type) {
 
2763
        case NPWindowTypeWindow:
 
2764
            return [self _createWindowedAGLContext];
 
2765
        
 
2766
        case NPWindowTypeDrawable:
 
2767
            return [self _createWindowlessAGLContext];
 
2768
        
 
2769
        default:
 
2770
            ASSERT_NOT_REACHED();
 
2771
            return NO;
 
2772
    }
 
2773
}
 
2774
 
 
2775
- (BOOL)_createWindowedAGLContext
 
2776
{
 
2777
    ASSERT(drawingModel == NPDrawingModelOpenGL);
 
2778
    ASSERT(!aglContext);
 
2779
    ASSERT(!aglWindow);
 
2780
    ASSERT([self window]);
 
2781
    
 
2782
    GLint pixelFormatAttributes[] = {
 
2783
        AGL_RGBA,
 
2784
        AGL_RED_SIZE, 8,
 
2785
        AGL_GREEN_SIZE, 8,
 
2786
        AGL_BLUE_SIZE, 8,
 
2787
        AGL_ALPHA_SIZE, 8,
 
2788
        AGL_DEPTH_SIZE, 32,
 
2789
        AGL_WINDOW,
 
2790
        AGL_ACCELERATED,
 
2791
        0
 
2792
    };
 
2793
    
 
2794
    // Choose AGL pixel format
 
2795
    AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL, 0, pixelFormatAttributes);
 
2796
    if (!pixelFormat) {
 
2797
        LOG_ERROR("Could not find suitable AGL pixel format: %s", aglErrorString(aglGetError()));
 
2798
        return NO;
 
2799
    }
 
2800
    
 
2801
    // Create AGL context
 
2802
    aglContext = aglCreateContext(pixelFormat, NULL);
 
2803
    aglDestroyPixelFormat(pixelFormat);
 
2804
    if (!aglContext) {
 
2805
        LOG_ERROR("Could not create AGL context: %s", aglErrorString(aglGetError()));
 
2806
        return NO;
 
2807
    }
 
2808
    
 
2809
    // Create AGL window
 
2810
    aglWindow = [[NSWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
 
2811
    if (!aglWindow) {
 
2812
        LOG_ERROR("Could not create window for AGL drawable.");
 
2813
        return NO;
 
2814
    }
 
2815
    
 
2816
    // AGL window should allow clicks to go through -- mouse events are tracked by WebCore
 
2817
    [aglWindow setIgnoresMouseEvents:YES];
 
2818
    
 
2819
    // Make sure the window is not opaque -- windowed plug-ins cannot layer with other page elements
 
2820
    [aglWindow setOpaque:YES];
 
2821
 
 
2822
    // Position and order in the AGL window
 
2823
    [self _reshapeAGLWindow];
 
2824
 
 
2825
    // Attach the AGL context to its window
 
2826
    GLboolean success;
 
2827
#ifdef AGL_VERSION_3_0
 
2828
    success = aglSetWindowRef(aglContext, (WindowRef)[aglWindow windowRef]);
 
2829
#else
 
2830
    success = aglSetDrawable(aglContext, (AGLDrawable)GetWindowPort((WindowRef)[aglWindow windowRef]));
 
2831
#endif
 
2832
    if (!success) {
 
2833
        LOG_ERROR("Could not set AGL drawable: %s", aglErrorString(aglGetError()));
 
2834
        aglDestroyContext(aglContext);
 
2835
        aglContext = NULL;
 
2836
        return NO;
 
2837
    }
 
2838
        
 
2839
    return YES;
 
2840
}
 
2841
 
 
2842
- (BOOL)_createWindowlessAGLContext
 
2843
{
 
2844
    ASSERT(drawingModel == NPDrawingModelOpenGL);
 
2845
    ASSERT(!aglContext);
 
2846
    ASSERT(!aglWindow);
 
2847
    
 
2848
    GLint pixelFormatAttributes[] = {
 
2849
        AGL_RGBA,
 
2850
        AGL_RED_SIZE, 8,
 
2851
        AGL_GREEN_SIZE, 8,
 
2852
        AGL_BLUE_SIZE, 8,
 
2853
        AGL_ALPHA_SIZE, 8,
 
2854
        AGL_DEPTH_SIZE, 32,
 
2855
        AGL_OFFSCREEN,
 
2856
        0
 
2857
    };
 
2858
 
 
2859
    // Choose AGL pixel format
 
2860
    AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL, 0, pixelFormatAttributes);
 
2861
    if (!pixelFormat) {
 
2862
        LOG_ERROR("Could not find suitable AGL pixel format: %s", aglErrorString(aglGetError()));
 
2863
        return NO;
 
2864
    }
 
2865
    
 
2866
    // Create AGL context
 
2867
    aglContext = aglCreateContext(pixelFormat, NULL);
 
2868
    aglDestroyPixelFormat(pixelFormat);
 
2869
    if (!aglContext) {
 
2870
        LOG_ERROR("Could not create AGL context: %s", aglErrorString(aglGetError()));
 
2871
        return NO;
 
2872
    }
 
2873
    
 
2874
    // Create offscreen buffer for AGL context
 
2875
    NSSize boundsSize = [self bounds].size;
 
2876
    GLvoid *offscreenBuffer = (GLvoid *)malloc(static_cast<size_t>(boundsSize.width * boundsSize.height * 4));
 
2877
    if (!offscreenBuffer) {
 
2878
        LOG_ERROR("Could not allocate offscreen buffer for AGL context");
 
2879
        aglDestroyContext(aglContext);
 
2880
        aglContext = NULL;
 
2881
        return NO;
 
2882
    }
 
2883
    
 
2884
    // Attach AGL context to offscreen buffer
 
2885
    CGLContextObj cglContext = [self _cglContext];
 
2886
    CGLError error = CGLSetOffScreen(cglContext, static_cast<long>(boundsSize.width), static_cast<long>(boundsSize.height), static_cast<long>(boundsSize.width * 4), offscreenBuffer);
 
2887
    if (error) {
 
2888
        LOG_ERROR("Could not set offscreen buffer for AGL context: %d", error);
 
2889
        aglDestroyContext(aglContext);
 
2890
        aglContext = NULL;
 
2891
        return NO;
 
2892
    }
 
2893
    
 
2894
    return YES;
 
2895
}
 
2896
 
 
2897
- (CGLContextObj)_cglContext
 
2898
{
 
2899
    ASSERT(drawingModel == NPDrawingModelOpenGL);
 
2900
 
 
2901
    CGLContextObj cglContext = NULL;
 
2902
    if (!aglGetCGLContext(aglContext, (void **)&cglContext) || !cglContext)
 
2903
        LOG_ERROR("Could not get CGL context for AGL context: %s", aglErrorString(aglGetError()));
 
2904
        
 
2905
    return cglContext;
 
2906
}
 
2907
 
 
2908
- (BOOL)_getAGLOffscreenBuffer:(GLvoid **)outBuffer width:(GLsizei *)outWidth height:(GLsizei *)outHeight
 
2909
{
 
2910
    ASSERT(drawingModel == NPDrawingModelOpenGL);
 
2911
    
 
2912
    if (outBuffer)
 
2913
        *outBuffer = NULL;
 
2914
    if (outWidth)
 
2915
        *outWidth = 0;
 
2916
    if (outHeight)
 
2917
        *outHeight = 0;
 
2918
    
 
2919
    // Only windowless plug-ins have offscreen buffers
 
2920
    if (window.type != NPWindowTypeDrawable)
 
2921
        return NO;
 
2922
    
 
2923
    CGLContextObj cglContext = [self _cglContext];
 
2924
    if (!cglContext)
 
2925
        return NO;
 
2926
    
 
2927
    GLsizei width, height;
 
2928
    GLint rowBytes;
 
2929
    void *offscreenBuffer = NULL;
 
2930
    CGLError error = CGLGetOffScreen(cglContext, &width, &height, &rowBytes, &offscreenBuffer);
 
2931
    if (error || !offscreenBuffer) {
 
2932
        LOG_ERROR("Could not get offscreen buffer for AGL context: %d", error);
 
2933
        return NO;
 
2934
    }
 
2935
    
 
2936
    if (outBuffer)
 
2937
        *outBuffer = offscreenBuffer;
 
2938
    if (outWidth)
 
2939
        *outWidth = width;
 
2940
    if (outHeight)
 
2941
        *outHeight = height;
 
2942
    
 
2943
    return YES;
 
2944
}
 
2945
 
 
2946
- (void)_destroyAGLContext
 
2947
{    
 
2948
    ASSERT(drawingModel == NPDrawingModelOpenGL);
 
2949
 
 
2950
    if (!aglContext)
 
2951
        return;
 
2952
 
 
2953
    if (aglContext) {
 
2954
        // If this is a windowless plug-in, free its offscreen buffer
 
2955
        GLvoid *offscreenBuffer;
 
2956
        if ([self _getAGLOffscreenBuffer:&offscreenBuffer width:NULL height:NULL])
 
2957
            free(offscreenBuffer);
 
2958
        
 
2959
        // Detach context from the AGL window
 
2960
#ifdef AGL_VERSION_3_0
 
2961
        aglSetWindowRef(aglContext, NULL);
 
2962
#else
 
2963
        aglSetDrawable(aglContext, NULL);
 
2964
#endif
 
2965
        
 
2966
        // Destroy the context
 
2967
        aglDestroyContext(aglContext);
 
2968
        aglContext = NULL;
 
2969
    }
 
2970
    
 
2971
    // Destroy the AGL window
 
2972
    if (aglWindow) {
 
2973
        [self _hideAGLWindow];
 
2974
        aglWindow = nil;
 
2975
    }
 
2976
}
 
2977
 
 
2978
- (void)_reshapeAGLWindow
 
2979
{
 
2980
    ASSERT(drawingModel == NPDrawingModelOpenGL);
 
2981
    
 
2982
    if (!aglContext)
 
2983
        return;
 
2984
 
 
2985
    switch (window.type) {
 
2986
        case NPWindowTypeWindow:
 
2987
        {
 
2988
            if (!aglWindow)
 
2989
                break;
 
2990
                
 
2991
            // The AGL window is being reshaped because the plugin view has moved.  Since the view has moved, it will soon redraw.
 
2992
            // We want the AGL window to update at the same time as its underlying view.  So, we disable screen updates until the
 
2993
            // plugin view's window flushes.
 
2994
            NSWindow *browserWindow = [self window];
 
2995
            ASSERT(browserWindow);
 
2996
            [browserWindow disableScreenUpdatesUntilFlush];
 
2997
 
 
2998
            // Add the AGL window as a child of the main window if necessary
 
2999
            if ([aglWindow parentWindow] != browserWindow)
 
3000
                [browserWindow addChildWindow:aglWindow ordered:NSWindowAbove];
 
3001
            
 
3002
            // Update the AGL window frame
 
3003
            NSRect aglWindowFrame = [self convertRect:[self visibleRect] toView:nil];
 
3004
            aglWindowFrame.origin = [browserWindow convertBaseToScreen:aglWindowFrame.origin];
 
3005
            [aglWindow setFrame:aglWindowFrame display:NO];
 
3006
            
 
3007
            // Update the AGL context
 
3008
            aglUpdateContext(aglContext);
 
3009
        }
 
3010
        break;
 
3011
        
 
3012
        case NPWindowTypeDrawable:
 
3013
        {
 
3014
            // Get offscreen buffer; we can skip this step if we don't have one yet
 
3015
            GLvoid *offscreenBuffer;
 
3016
            GLsizei width, height;
 
3017
            if (![self _getAGLOffscreenBuffer:&offscreenBuffer width:&width height:&height] || !offscreenBuffer)
 
3018
                break;
 
3019
            
 
3020
            // Don't resize the offscreen buffer if it's already the same size as the view bounds
 
3021
            NSSize boundsSize = [self bounds].size;
 
3022
            if (boundsSize.width == width && boundsSize.height == height)
 
3023
                break;
 
3024
            
 
3025
            // Resize the offscreen buffer
 
3026
            offscreenBuffer = realloc(offscreenBuffer, static_cast<size_t>(boundsSize.width * boundsSize.height * 4));
 
3027
            if (!offscreenBuffer) {
 
3028
                LOG_ERROR("Could not allocate offscreen buffer for AGL context");
 
3029
                break;
 
3030
            }
 
3031
 
 
3032
            // Update the offscreen 
 
3033
            CGLContextObj cglContext = [self _cglContext];
 
3034
            CGLError error = CGLSetOffScreen(cglContext, static_cast<long>(boundsSize.width), static_cast<long>(boundsSize.height), static_cast<long>(boundsSize.width * 4), offscreenBuffer);
 
3035
            if (error) {
 
3036
                LOG_ERROR("Could not set offscreen buffer for AGL context: %d", error);
 
3037
                break;
 
3038
            }
 
3039
 
 
3040
            // Update the AGL context
 
3041
            aglUpdateContext(aglContext);
 
3042
        }
 
3043
        break;
 
3044
        
 
3045
        default:
 
3046
            ASSERT_NOT_REACHED();
 
3047
        break;
 
3048
    }
 
3049
}
 
3050
 
 
3051
- (void)_hideAGLWindow
 
3052
{
 
3053
    ASSERT(drawingModel == NPDrawingModelOpenGL);
 
3054
    
 
3055
    if (!aglWindow)
 
3056
        return;
 
3057
    
 
3058
    // aglWindow should only be set for a windowed OpenGL plug-in
 
3059
    ASSERT(window.type == NPWindowTypeWindow);
 
3060
    
 
3061
    NSWindow *parentWindow = [aglWindow parentWindow];
 
3062
    if (parentWindow) {
 
3063
        // Disable screen updates so that this AGL window orders out atomically with other plugins' AGL windows
 
3064
        [parentWindow disableScreenUpdatesUntilFlush];
 
3065
        ASSERT(parentWindow == [self window]);
 
3066
        [parentWindow removeChildWindow:aglWindow];
 
3067
    }
 
3068
    [aglWindow orderOut:nil];
 
3069
}
 
3070
 
 
3071
- (NSImage *)_aglOffscreenImageForDrawingInRect:(NSRect)drawingInRect
 
3072
{
 
3073
    ASSERT(drawingModel == NPDrawingModelOpenGL);
 
3074
 
 
3075
    CGLContextObj cglContext = [self _cglContext];
 
3076
    if (!cglContext)
 
3077
        return nil;
 
3078
 
 
3079
    // Get the offscreen buffer
 
3080
    GLvoid *offscreenBuffer;
 
3081
    GLsizei width, height;
 
3082
    if (![self _getAGLOffscreenBuffer:&offscreenBuffer width:&width height:&height])
 
3083
        return nil;
 
3084
 
 
3085
    unsigned char *plane = (unsigned char *)offscreenBuffer;
 
3086
 
 
3087
#if defined(__i386__) || defined(__x86_64__)
 
3088
    // Make rect inside the offscreen buffer because we're about to directly modify the bits inside drawingInRect
 
3089
    NSRect rect = NSIntegralRect(NSIntersectionRect(drawingInRect, NSMakeRect(0, 0, width, height)));
 
3090
 
 
3091
    // The offscreen buffer, being an OpenGL framebuffer, is in BGRA format on x86.  We need to swap the blue and red channels before
 
3092
    // wrapping the buffer in an NSBitmapImageRep, which only supports RGBA and ARGB.
 
3093
    // On PowerPC, the OpenGL framebuffer is in ARGB format.  Since that is a format that NSBitmapImageRep supports, all that is
 
3094
    // needed on PowerPC is to pass the NSAlphaFirstBitmapFormat flag when creating the NSBitmapImageRep.  On x86, we need to swap the
 
3095
    // framebuffer color components such that they are in ARGB order, as they are on PowerPC.
 
3096
    // If only a small region of the plug-in is being redrawn, then it would be a waste to convert the entire image from BGRA to ARGB.
 
3097
    // Since we know what region of the image will ultimately be drawn to screen (drawingInRect), we restrict the channel swapping to
 
3098
    // just that region within the offscreen buffer.
 
3099
    if (!WebConvertBGRAToARGB(plane, width * 4, (int)rect.origin.x, (int)rect.origin.y, (int)rect.size.width, (int)rect.size.height))
 
3100
        return nil;
 
3101
#endif /* defined(__i386__) || defined(__x86_64__) */
 
3102
    
 
3103
    NSBitmapImageRep *aglBitmap = [[NSBitmapImageRep alloc]
 
3104
        initWithBitmapDataPlanes:&plane
 
3105
                      pixelsWide:width
 
3106
                      pixelsHigh:height
 
3107
                   bitsPerSample:8
 
3108
                 samplesPerPixel:4
 
3109
                        hasAlpha:YES
 
3110
                        isPlanar:NO
 
3111
                  colorSpaceName:NSDeviceRGBColorSpace
 
3112
                    bitmapFormat:NSAlphaFirstBitmapFormat
 
3113
                     bytesPerRow:width * 4
 
3114
                    bitsPerPixel:32];
 
3115
    if (!aglBitmap) {
 
3116
        LOG_ERROR("Could not create bitmap for AGL offscreen buffer");
 
3117
        return nil;
 
3118
    }
 
3119
 
 
3120
    // Wrap the bitmap in an NSImage.  This allocation isn't very expensive -- the actual image data is already in the bitmap rep
 
3121
    NSImage *aglImage = [[[NSImage alloc] initWithSize:[aglBitmap size]] autorelease];
 
3122
    [aglImage addRepresentation:aglBitmap];
 
3123
    [aglBitmap release];
 
3124
    
 
3125
    return aglImage;
 
3126
}
 
3127
 
 
3128
- (void)_redeliverStream
 
3129
{
 
3130
    if ([self dataSource] && [self isStarted]) {
 
3131
        // Deliver what has not been passed to the plug-in up to this point.
 
3132
        if (_dataLengthReceived > 0) {
 
3133
            NSData *data = [[[self dataSource] data] subdataWithRange:NSMakeRange(0, _dataLengthReceived)];
 
3134
            _dataLengthReceived = 0;
 
3135
            [self pluginView:self receivedData:data];
 
3136
            if (![[self dataSource] isLoading]) {
 
3137
                if (_error)
 
3138
                    [self pluginView:self receivedError:_error];
 
3139
                else
 
3140
                    [self pluginViewFinishedLoading:self];
 
3141
            }
 
3142
        }
 
3143
    }
 
3144
}
 
3145
 
 
3146
@end
 
3147
 
 
3148
@implementation NSData (PluginExtras)
 
3149
 
 
3150
- (BOOL)_web_startsWithBlankLine
 
3151
{
 
3152
    return [self length] > 0 && ((const char *)[self bytes])[0] == '\n';
 
3153
}
 
3154
 
 
3155
 
 
3156
- (NSInteger)_web_locationAfterFirstBlankLine
 
3157
{
 
3158
    const char *bytes = (const char *)[self bytes];
 
3159
    unsigned length = [self length];
 
3160
    
 
3161
    unsigned i;
 
3162
    for (i = 0; i < length - 4; i++) {
 
3163
        
 
3164
        //  Support for Acrobat. It sends "\n\n".
 
3165
        if (bytes[i] == '\n' && bytes[i+1] == '\n') {
 
3166
            return i+2;
 
3167
        }
 
3168
        
 
3169
        // Returns the position after 2 CRLF's or 1 CRLF if it is the first line.
 
3170
        if (bytes[i] == '\r' && bytes[i+1] == '\n') {
 
3171
            i += 2;
 
3172
            if (i == 2) {
 
3173
                return i;
 
3174
            } else if (bytes[i] == '\n') {
 
3175
                // Support for Director. It sends "\r\n\n" (3880387).
 
3176
                return i+1;
 
3177
            } else if (bytes[i] == '\r' && bytes[i+1] == '\n') {
 
3178
                // Support for Flash. It sends "\r\n\r\n" (3758113).
 
3179
                return i+2;
 
3180
            }
 
3181
        }
 
3182
    }
 
3183
    return NSNotFound;
 
3184
}
 
3185
 
 
3186
@end
 
3187
#endif