2
* Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
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.
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.
31
#import "WebBaseNetscapePluginView.h"
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>
68
using namespace WebCore;
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
74
#define LoginWindowDidSwitchFromUserNotification @"WebLoginWindowDidSwitchFromUserNotification"
75
#define LoginWindowDidSwitchToUserNotification @"WebLoginWindowDidSwitchToUserNotification"
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;
94
static WebBaseNetscapePluginView *currentPluginView = nil;
96
typedef struct OpaquePortState* PortState;
98
#ifndef NP_NO_QUICKDRAW
100
// QuickDraw is not available in 64-bit
106
RgnHandle oldClipRegion;
107
RgnHandle oldVisibleRegion;
108
RgnHandle clipRegion;
112
#endif /* NP_NO_QUICKDRAW */
115
CGContextRef context;
119
AGLContext oldContext;
122
@interface WebPluginRequest : NSObject
124
NSURLRequest *_request;
125
NSString *_frameName;
127
BOOL _didStartFromUserGesture;
128
BOOL _sendNotification;
131
- (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture;
133
- (NSURLRequest *)request;
134
- (NSString *)frameName;
135
- (void *)notifyData;
136
- (BOOL)isCurrentEventUserGesture;
137
- (BOOL)sendNotification;
141
@interface NSData (WebPluginDataExtras)
142
- (BOOL)_web_startsWithBlankLine;
143
- (NSInteger)_web_locationAfterFirstBlankLine;
146
static OSStatus TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *pluginView);
148
@interface WebBaseNetscapePluginView (ForwardDeclarations)
149
- (void)setWindowIfNecessary;
150
- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification;
153
@implementation WebBaseNetscapePluginView
157
#ifndef BUILDING_ON_TIGER
158
WebCoreObjCFinalizeOnMainThread(self);
160
WKSendUserChangeNotifications();
165
+ (void)getCarbonEvent:(EventRecord *)carbonEvent
167
carbonEvent->what = nullEvent;
168
carbonEvent->message = 0;
169
carbonEvent->when = TickCount();
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();
176
carbonEvent->modifiers |= btnState;
179
- (void)getCarbonEvent:(EventRecord *)carbonEvent
181
[[self class] getCarbonEvent:carbonEvent];
184
- (EventModifiers)modifiersForEvent:(NSEvent *)event
186
EventModifiers modifiers;
187
unsigned int modifierFlags = [event modifierFlags];
188
NSEventType eventType = [event type];
192
if (eventType != NSLeftMouseDown && eventType != NSRightMouseDown)
193
modifiers |= btnState;
195
if (modifierFlags & NSCommandKeyMask)
198
if (modifierFlags & NSShiftKeyMask)
199
modifiers |= shiftKey;
201
if (modifierFlags & NSAlphaShiftKeyMask)
202
modifiers |= alphaLock;
204
if (modifierFlags & NSAlternateKeyMask)
205
modifiers |= optionKey;
207
if (modifierFlags & NSControlKeyMask || eventType == NSRightMouseDown)
208
modifiers |= controlKey;
213
- (void)getCarbonEvent:(EventRecord *)carbonEvent withEvent:(NSEvent *)cocoaEvent
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());
221
NSPoint where = [[cocoaEvent window] convertBaseToScreen:[cocoaEvent locationInWindow]];
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];
231
- (BOOL)superviewsHaveSuperviews
233
NSView *contentView = [[self window] contentView];
235
for (view = self; view != nil; view = [view superview]) {
236
if (view == contentView) {
243
#ifndef NP_NO_QUICKDRAW
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
250
ASSERT(drawingModel == NPDrawingModelQuickDraw);
252
NSWindow *currentWindow = [self currentWindow];
253
if ([currentWindow isKindOfClass:objc_getClass("NSCarbonWindow")])
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
262
SetPort(GetWindowPort((WindowRef)[currentWindow windowRef]));
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));
270
static UInt32 getQDPixelFormatForBitmapContext(CGContextRef context)
272
UInt32 byteOrder = CGBitmapContextGetBitmapInfo(context) & kCGBitmapByteOrderMask;
273
if (byteOrder == kCGBitmapByteOrderDefault)
274
switch (CGBitmapContextGetBitsPerPixel(context)) {
276
byteOrder = kCGBitmapByteOrder16Host;
279
byteOrder = kCGBitmapByteOrder32Host;
283
case kCGBitmapByteOrder16Little:
284
return k16LE555PixelFormat;
285
case kCGBitmapByteOrder32Little:
286
return k32BGRAPixelFormat;
287
case kCGBitmapByteOrder16Big:
288
return k16BE555PixelFormat;
289
case kCGBitmapByteOrder32Big:
290
return k32ARGBPixelFormat;
292
ASSERT_NOT_REACHED();
296
static inline void getNPRect(const CGRect& cgr, NPRect& npr)
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));
306
static inline void getNPRect(const NSRect& nr, NPRect& npr)
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));
314
- (NSRect)visibleRect
316
// WebCore may impose an additional clip (via CSS overflow or clip properties). Fetch
318
return NSIntersectionRect([self convertRect:[element _windowClipRect] fromView:nil], [super visibleRect]);
321
- (PortState)saveAndSetNewPortStateForUpdate:(BOOL)forUpdate
323
ASSERT([self currentWindow] != nil);
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];
332
WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef];
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];
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);
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) {
348
CGrafPtr port = GetWindowPort(windowRef);
349
GetPortBounds(port, &portBounds);
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;
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));
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]
374
|| ![self superviewsHaveSuperviews]
375
|| [self isHiddenOrHasHiddenAncestor]) {
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.
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.
385
if (window.width <= 0)
386
window.width = specifiedWidth > 0 ? specifiedWidth : 100;
387
if (window.height <= 0)
388
window.height = specifiedHeight > 0 ? specifiedHeight : 100;
390
window.clipRect.bottom = window.clipRect.top;
391
window.clipRect.left = window.clipRect.right;
393
getNPRect(visibleRectInWindow, window.clipRect);
396
// Save the port state, set up the port for entry into the plugin
398
switch (drawingModel) {
399
#ifndef NP_NO_QUICKDRAW
400
case NPDrawingModelQuickDraw: {
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;
410
PortState_QD *qdPortState = (PortState_QD*)malloc(sizeof(PortState_QD));
411
portState = (PortState)qdPortState;
413
GetGWorld(&qdPortState->oldPort, &qdPortState->oldDevice);
415
qdPortState->oldOrigin.h = portBounds.left;
416
qdPortState->oldOrigin.v = portBounds.top;
418
qdPortState->oldClipRegion = NewRgn();
419
GetPortClipRegion(port, qdPortState->oldClipRegion);
421
qdPortState->oldVisibleRegion = NewRgn();
422
GetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
424
RgnHandle clipRegion = NewRgn();
425
qdPortState->clipRegion = clipRegion;
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);
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);
448
DisposeGWorld(offscreenGWorld);
449
offscreenGWorld = newOffscreenGWorld;
451
SetGWorld(offscreenGWorld, NULL);
453
port = offscreenGWorld;
455
nPort.qdPort.port = port;
456
boundsInWindow = [self bounds];
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);
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);
469
nPort.qdPort.portx = static_cast<int32>(-boundsInWindow.origin.x + origin.x);
470
nPort.qdPort.porty = static_cast<int32>(-boundsInWindow.origin.y - origin.y);
473
window.window = &nPort;
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);
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);
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.
489
RgnHandle viewClipRegion = NewRgn();
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];
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)));
506
// Union this dirty rect with the rest of the dirty rects
507
UnionRgn(viewClipRegion, dirtyRectRegion, viewClipRegion);
508
DisposeRgn(dirtyRectRegion);
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);
517
// Switch to the port and set it up.
520
ForeColor(blackColor);
521
BackColor(whiteColor);
522
SetOrigin(nPort.qdPort.portx, nPort.qdPort.porty);
523
SetPortClipRegion(nPort.qdPort.port, clipRegion);
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);
533
qdPortState->forUpdate = forUpdate;
536
#endif /* NP_NO_QUICKDRAW */
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);
542
CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]);
544
PortState_CG *cgPortState = (PortState_CG *)malloc(sizeof(PortState_CG));
545
portState = (PortState)cgPortState;
546
cgPortState->context = context;
548
// Update the plugin's window/context
549
nPort.cgPort.window = windowRef;
550
nPort.cgPort.context = context;
551
window.window = &nPort.cgPort;
553
// Save current graphics context's state; will be restored by -restorePortState:
554
CGContextSaveGState(context);
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;
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);
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);
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;
580
// Create AGL context if needed
581
if (![self _createAGLContextIfNeeded]) {
582
LOG_ERROR("Could not create AGL context");
586
// Update the plugin's window/context
587
nPort.aglPort.window = windowRef;
588
nPort.aglPort.context = [self _cglContext];
589
window.window = &nPort.aglPort;
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);
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);
605
case NPWindowTypeDrawable: {
606
GLsizei width, height;
607
if ([self _getAGLOffscreenBuffer:NULL width:&width height:&height])
608
glViewport(0, 0, width, height);
613
ASSERT_NOT_REACHED();
620
ASSERT_NOT_REACHED();
628
- (PortState)saveAndSetNewPortState
630
return [self saveAndSetNewPortStateForUpdate:NO];
633
- (void)restorePortState:(PortState)portState
635
ASSERT([self currentWindow]);
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);
647
if (qdPortState->forUpdate)
648
ValidWindowRgn(windowRef, qdPortState->clipRegion);
650
SetOrigin(qdPortState->oldOrigin.h, qdPortState->oldOrigin.v);
652
SetPortClipRegion(port, qdPortState->oldClipRegion);
653
if (qdPortState->forUpdate)
654
SetPortVisibleRegion(port, qdPortState->oldVisibleRegion);
656
DisposeRgn(qdPortState->oldClipRegion);
657
DisposeRgn(qdPortState->oldVisibleRegion);
658
DisposeRgn(qdPortState->clipRegion);
660
SetGWorld(qdPortState->oldPort, qdPortState->oldDevice);
663
#endif /* NP_NO_QUICKDRAW */
665
case NPDrawingModelCoreGraphics:
666
ASSERT([NSView focusView] == self);
667
ASSERT(((PortState_CG *)portState)->context == nPort.cgPort.context);
668
CGContextRestoreGState(nPort.cgPort.context);
671
case NPDrawingModelOpenGL:
672
aglSetCurrentContext(((PortState_GL *)portState)->oldContext);
676
ASSERT_NOT_REACHED();
681
- (BOOL)sendEvent:(EventRecord *)event
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;
693
suspendKeyUpEvents = NO;
698
ASSERT(NPP_HandleEvent);
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.
707
Frame* frame = core([self webFrame]);
710
Page* page = frame->page();
714
bool wasDeferring = page->defersLoading();
716
page->setDefersLoading(true);
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);
721
BOOL updating = event->what == updateEvt;
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];
728
// We may have changed the window, so inform the plug-in.
729
[self setWindowIfNecessary];
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 };
741
ForeColor(blackColor);
745
// Temporarily retain self in case the plug-in view is released while sending an event.
746
[[self retain] autorelease];
749
[self willCallPlugInFunction];
751
KJS::JSLock::DropAllLocks dropAllLocks;
752
acceptedEvent = NPP_HandleEvent(plugin, event);
754
[self didCallPlugInFunction];
756
currentEventIsUserGesture = NO;
759
if ([self currentWindow])
760
[self restorePortState:portState];
765
page->setDefersLoading(false);
767
return acceptedEvent;
770
- (void)sendActivateEvent:(BOOL)activate
774
[self getCarbonEvent:&event];
775
event.what = activateEvt;
776
WindowRef windowRef = (WindowRef)[[self window] windowRef];
777
event.message = (unsigned long)windowRef;
779
event.modifiers |= activeFlag;
783
acceptedEvent = [self sendEvent:&event];
785
LOG(PluginEvents, "NPP_HandleEvent(activateEvent): %d isActive: %d", acceptedEvent, activate);
788
- (BOOL)sendUpdateEvent
792
[self getCarbonEvent:&event];
793
event.what = updateEvt;
794
WindowRef windowRef = (WindowRef)[[self window] windowRef];
795
event.message = (unsigned long)windowRef;
797
BOOL acceptedEvent = [self sendEvent:&event];
799
LOG(PluginEvents, "NPP_HandleEvent(updateEvt): %d", acceptedEvent);
801
return acceptedEvent;
808
[self getCarbonEvent:&event];
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);
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?
821
[self sendEvent:&event];
824
- (void)stopNullEvents
826
[nullEventTimer invalidate];
827
[nullEventTimer release];
828
nullEventTimer = nil;
831
- (void)restartNullEvents
833
ASSERT([self window]);
836
[self stopNullEvents];
838
if (!isStarted || [[self window] isMiniaturized])
841
NSTimeInterval interval;
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
848
selector:@selector(sendNullEvent)
850
repeats:YES] retain];
853
- (BOOL)acceptsFirstResponder
858
- (void)installKeyEventHandler
860
static const EventTypeSpec sTSMEvents[] =
862
{ kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
865
if (!keyEventHandler) {
866
InstallEventHandler(GetWindowEventTarget((WindowRef)[[self window] windowRef]),
867
NewEventHandlerUPP(TSMEventHandler),
868
GetEventTypeCount(sTSMEvents),
875
- (void)removeKeyEventHandler
877
if (keyEventHandler) {
878
RemoveEventHandler(keyEventHandler);
879
keyEventHandler = NULL;
883
- (void)setHasFocus:(BOOL)flag
885
if (hasFocus != flag) {
888
[self getCarbonEvent:&event];
891
event.what = getFocusEvent;
892
acceptedEvent = [self sendEvent:&event];
893
LOG(PluginEvents, "NPP_HandleEvent(getFocusEvent): %d", acceptedEvent);
894
[self installKeyEventHandler];
896
event.what = loseFocusEvent;
897
acceptedEvent = [self sendEvent:&event];
898
LOG(PluginEvents, "NPP_HandleEvent(loseFocusEvent): %d", acceptedEvent);
899
[self removeKeyEventHandler];
904
- (BOOL)becomeFirstResponder
906
[self setHasFocus:YES];
910
- (BOOL)resignFirstResponder
912
[self setHasFocus:NO];
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
920
[self mouseDown:theEvent];
923
- (void)rightMouseUp:(NSEvent *)theEvent
925
[self mouseUp:theEvent];
928
- (void)mouseDown:(NSEvent *)theEvent
932
[self getCarbonEvent:&event withEvent:theEvent];
933
event.what = mouseDown;
936
acceptedEvent = [self sendEvent:&event];
938
LOG(PluginEvents, "NPP_HandleEvent(mouseDown): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
941
- (void)mouseUp:(NSEvent *)theEvent
945
[self getCarbonEvent:&event withEvent:theEvent];
946
event.what = mouseUp;
949
acceptedEvent = [self sendEvent:&event];
951
LOG(PluginEvents, "NPP_HandleEvent(mouseUp): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
954
- (void)mouseEntered:(NSEvent *)theEvent
958
[self getCarbonEvent:&event withEvent:theEvent];
959
event.what = adjustCursorEvent;
962
acceptedEvent = [self sendEvent:&event];
964
LOG(PluginEvents, "NPP_HandleEvent(mouseEntered): %d", acceptedEvent);
967
- (void)mouseExited:(NSEvent *)theEvent
971
[self getCarbonEvent:&event withEvent:theEvent];
972
event.what = adjustCursorEvent;
975
acceptedEvent = [self sendEvent:&event];
977
LOG(PluginEvents, "NPP_HandleEvent(mouseExited): %d", acceptedEvent);
979
// Set cursor back to arrow cursor.
980
[[NSCursor arrowCursor] set];
983
- (void)mouseDragged:(NSEvent *)theEvent
985
// Do nothing so that other responders don't respond to the drag that initiated in this view.
988
- (UInt32)keyMessageForEvent:(NSEvent *)event
990
NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())];
995
[data getBytes:&characterCode length:1];
996
UInt16 keyCode = [event keyCode];
997
return keyCode << 8 | characterCode;
1000
- (void)keyUp:(NSEvent *)theEvent
1002
WKSendKeyEventToTSM(theEvent);
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) {
1009
[self getCarbonEvent:&event withEvent:theEvent];
1012
if (event.message == 0) {
1013
event.message = [self keyMessageForEvent:theEvent];
1016
[self sendEvent:&event];
1020
- (void)keyDown:(NSEvent *)theEvent
1022
suspendKeyUpEvents = YES;
1023
WKSendKeyEventToTSM(theEvent);
1026
static OSStatus TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *pluginView)
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);
1035
// Two-pass read to allocate/extract Mac charCodes
1037
status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL);
1038
if (status != noErr) {
1039
LOG_ERROR("GetEventParameter failed with error: %d", status);
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);
1050
EventRef cloneEvent = CopyEvent(rawKeyEventRef);
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);
1060
EventRecord eventRec;
1061
if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
1063
acceptedEvent = [(WebBaseNetscapePluginView *)pluginView sendEvent:&eventRec];
1065
LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu",
1066
acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask));
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.
1074
ReleaseEvent(cloneEvent);
1081
// Fake up command-modified events so cut, copy, paste and select all menus work.
1082
- (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character
1085
[self getCarbonEvent:&event];
1086
event.what = keyDown;
1087
event.modifiers |= cmdKey;
1088
event.message = keyCode << 8 | character;
1089
[self sendEvent:&event];
1092
- (void)cut:(id)sender
1094
[self sendModifierEventWithKeyCode:7 character:'x'];
1097
- (void)copy:(id)sender
1099
[self sendModifierEventWithKeyCode:8 character:'c'];
1102
- (void)paste:(id)sender
1104
[self sendModifierEventWithKeyCode:9 character:'v'];
1107
- (void)selectAll:(id)sender
1109
[self sendModifierEventWithKeyCode:0 character:'a'];
1112
#pragma mark WEB_NETSCAPE_PLUGIN
1114
- (BOOL)isNewWindowEqualToOldWindow
1116
if (window.x != lastSetWindow.x)
1118
if (window.y != lastSetWindow.y)
1120
if (window.width != lastSetWindow.width)
1122
if (window.height != lastSetWindow.height)
1124
if (window.clipRect.top != lastSetWindow.clipRect.top)
1126
if (window.clipRect.left != lastSetWindow.clipRect.left)
1128
if (window.clipRect.bottom != lastSetWindow.clipRect.bottom)
1130
if (window.clipRect.right != lastSetWindow.clipRect.right)
1132
if (window.type != lastSetWindow.type)
1135
switch (drawingModel) {
1136
#ifndef NP_NO_QUICKDRAW
1137
case NPDrawingModelQuickDraw:
1138
if (nPort.qdPort.portx != lastSetPort.qdPort.portx)
1140
if (nPort.qdPort.porty != lastSetPort.qdPort.porty)
1142
if (nPort.qdPort.port != lastSetPort.qdPort.port)
1145
#endif /* NP_NO_QUICKDRAW */
1147
case NPDrawingModelCoreGraphics:
1148
if (nPort.cgPort.window != lastSetPort.cgPort.window)
1150
if (nPort.cgPort.context != lastSetPort.cgPort.context)
1154
case NPDrawingModelOpenGL:
1155
if (nPort.aglPort.window != lastSetPort.aglPort.window)
1157
if (nPort.aglPort.context != lastSetPort.aglPort.context)
1162
ASSERT_NOT_REACHED();
1169
- (void)updateAndSetWindow
1171
if (drawingModel == NPDrawingModelCoreGraphics || drawingModel == NPDrawingModelOpenGL) {
1172
// Can only update CoreGraphics and OpenGL plugins while redrawing the plugin view
1173
[self setNeedsDisplay:YES];
1177
// Can't update the plugin if it has not started (or has been stopped)
1181
PortState portState = [self saveAndSetNewPortState];
1183
[self setWindowIfNecessary];
1184
[self restorePortState:portState];
1189
- (void)setWindowIfNecessary
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.
1201
ASSERT(!inSetWindow);
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);
1208
[self willCallPlugInFunction];
1210
KJS::JSLock::DropAllLocks dropAllLocks;
1211
npErr = NPP_SetWindow(plugin, &window);
1213
[self didCallPlugInFunction];
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);
1223
#endif /* NP_NO_QUICKDRAW */
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);
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);
1236
ASSERT_NOT_REACHED();
1239
#endif /* !defined(NDEBUG) */
1241
lastSetWindow = window;
1242
lastSetPort = nPort;
1246
- (void)removeTrackingRect
1249
[self removeTrackingRect:trackingTag];
1252
// Do the following after setting trackingTag to 0 so we don't re-enter.
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];
1260
- (void)resetTrackingRect
1262
[self removeTrackingRect];
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];
1270
+ (void)setCurrentPluginView:(WebBaseNetscapePluginView *)view
1272
currentPluginView = view;
1275
+ (WebBaseNetscapePluginView *)currentPluginView
1277
return currentPluginView;
1287
if (_loadManually) {
1288
[self _redeliverStream];
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];
1301
- (void)addWindowObservers
1303
ASSERT([self window]);
1305
NSWindow *theWindow = [self window];
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];
1319
[notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchFromUser:)
1320
name:LoginWindowDidSwitchFromUserNotification object:nil];
1321
[notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchToUser:)
1322
name:LoginWindowDidSwitchToUserNotification object:nil];
1325
- (void)removeWindowObservers
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];
1339
ASSERT([self currentWindow]);
1344
if (![self canStart])
1347
ASSERT([self webView]);
1349
if (![[[self webView] preferences] arePlugInsEnabled])
1352
// Open the plug-in package so it remains loaded while our plugin uses it
1353
[pluginPackage open];
1355
// Initialize drawingModel to an invalid value so that we can detect when the plugin does not specify a drawingModel
1356
drawingModel = (NPDrawingModel)-1;
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;
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];
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;
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];
1387
[self updateAndSetWindow];
1389
if ([self window]) {
1390
[self addWindowObservers];
1391
if ([[self window] isKeyWindow]) {
1392
[self sendActivateEvent:YES];
1394
[self restartNullEvents];
1397
[self resetTrackingRect];
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;
1415
[self removeTrackingRect];
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];
1428
// Stop the null events
1429
[self stopNullEvents];
1431
// Set cursor back to arrow cursor
1432
[[NSCursor arrowCursor] set];
1434
// Stop notifications and callbacks.
1435
[self removeWindowObservers];
1436
[[pendingFrameLoads allKeys] makeObjectsPerformSelector:@selector(_setInternalLoadDelegate:) withObject:nil];
1437
[NSObject cancelPreviousPerformRequestsWithTarget:self];
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;
1442
[self _destroyPlugin];
1443
[pluginPackage close];
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];
1449
if (drawingModel == NPDrawingModelOpenGL)
1450
[self _destroyAGLContext];
1458
- (WebDataSource *)dataSource
1460
WebFrame *webFrame = kit(core(element)->document()->frame());
1461
return [webFrame _dataSource];
1464
- (WebFrame *)webFrame
1466
return [[self dataSource] webFrame];
1469
- (WebView *)webView
1471
return [[self webFrame] webView];
1474
- (NSWindow *)currentWindow
1476
return [self window] ? [self window] : [[self webView] hostWindow];
1484
- (WebNetscapePluginPackage *)pluginPackage
1486
return pluginPackage;
1489
- (void)setPluginPackage:(WebNetscapePluginPackage *)thePluginPackage;
1491
[thePluginPackage retain];
1492
[pluginPackage release];
1493
pluginPackage = thePluginPackage;
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];
1510
- (void)setMIMEType:(NSString *)theMIMEType
1512
NSString *type = [theMIMEType copy];
1517
- (void)setBaseURL:(NSURL *)theBaseURL
1519
[theBaseURL retain];
1521
baseURL = theBaseURL;
1524
- (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values;
1526
ASSERT([keys count] == [values count]);
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.
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]);
1540
cAttributes = (char **)malloc([keys count] * sizeof(char *));
1541
cValues = (char **)malloc([values count] * sizeof(char *));
1544
BOOL isWMP = [[[pluginPackage bundle] bundleIdentifier] isEqualToString:@"com.microsoft.WMP.defaultplugin"];
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];
1556
// Avoid Window Media Player crash when these attributes are present.
1557
if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) {
1560
cAttributes[argsCount] = strdup([key UTF8String]);
1561
cValues[argsCount] = strdup([value UTF8String]);
1562
LOG(Plugins, "%@ = %@", key, value);
1567
- (void)setMode:(int)theMode
1574
- (id)initWithFrame:(NSRect)frame
1575
pluginPackage:(WebNetscapePluginPackage *)thePluginPackage
1577
baseURL:(NSURL *)theBaseURL
1578
MIMEType:(NSString *)MIME
1579
attributeKeys:(NSArray *)keys
1580
attributeValues:(NSArray *)values
1581
loadManually:(BOOL)loadManually
1582
DOMElement:(DOMElement *)anElement
1584
[super initWithFrame:frame];
1586
streams = [[NSMutableArray alloc] init];
1587
pendingFrameLoads = [[NSMutableDictionary alloc] init];
1589
// load the plug-in if it is not already loaded
1590
if (![thePluginPackage load]) {
1594
[self setPluginPackage:thePluginPackage];
1596
element = [anElement retain];
1597
sourceURL = [theURL retain];
1599
[self setMIMEType:MIME];
1600
[self setBaseURL:theBaseURL];
1601
[self setAttributeKeys:keys andValues:values];
1603
[self setMode:NP_FULL];
1605
[self setMode:NP_EMBED];
1607
_loadManually = loadManually;
1612
- (id)initWithFrame:(NSRect)frame
1614
ASSERT_NOT_REACHED();
1620
#ifndef NP_NO_QUICKDRAW
1621
if (offscreenGWorld)
1622
DisposeGWorld(offscreenGWorld);
1626
for (i = 0; i < argsCount; i++) {
1627
free(cAttributes[i]);
1634
- (void)disconnectStream:(WebBaseNetscapePluginStream*)stream
1636
[streams removeObjectIdenticalTo:stream];
1643
[sourceURL release];
1644
[_manualStream release];
1647
[pluginPackage release];
1651
[pendingFrameLoads release];
1656
ASSERT(!aglContext);
1665
ASSERT_MAIN_THREAD();
1673
- (void)drawRect:(NSRect)rect
1679
if ([NSGraphicsContext currentContextDrawingToScreen])
1680
[self sendUpdateEvent];
1682
NSBitmapImageRep *printedPluginBitmap = [self _printedPluginBitmap];
1683
if (printedPluginBitmap) {
1684
// Flip the bitmap before drawing because the QuickDraw port is flipped relative
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);
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);
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);
1723
[super renewGState];
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];
1732
#ifndef NP_NO_QUICKDRAW
1733
-(void)tellQuickTimeToChill
1735
ASSERT(drawingModel == NPDrawingModelQuickDraw);
1737
// Make a call to the secret QuickDraw API that makes QuickTime calm down.
1738
WindowRef windowRef = (WindowRef)[[self window] windowRef];
1742
CGrafPtr port = GetWindowPort(windowRef);
1744
GetPortBounds(port, &bounds);
1745
WKCallDrawingNotification(port, &bounds);
1747
#endif /* NP_NO_QUICKDRAW */
1749
- (void)viewWillMoveToWindow:(NSWindow *)newWindow
1751
#ifndef NP_NO_QUICKDRAW
1752
if (drawingModel == NPDrawingModelQuickDraw)
1753
[self tellQuickTimeToChill];
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];
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];
1765
// Hide the AGL child window
1766
if (drawingModel == NPDrawingModelOpenGL)
1767
[self _hideAGLWindow];
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];
1773
// View will have no associated windows.
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];
1783
- (void)viewWillMoveToSuperview:(NSView *)newSuperview
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.
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];
1797
- (void)viewDidMoveToWindow
1799
[self resetTrackingRect];
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
1809
// View moved to an actual window. Start it if not already started.
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];
1821
- (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow
1823
if (!hostWindow && ![self window]) {
1824
// View will have no associated windows.
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];
1832
- (void)viewDidMoveToHostWindow
1834
if ([[self webView] hostWindow]) {
1835
// View now has an associated window. Start it if not already started.
1840
#pragma mark NOTIFICATIONS
1842
- (void)windowWillClose:(NSNotification *)notification
1847
- (void)windowBecameKey:(NSNotification *)notification
1849
[self sendActivateEvent:YES];
1850
[self setNeedsDisplay:YES];
1851
[self restartNullEvents];
1852
SetUserFocusWindow((WindowRef)[[self window] windowRef]);
1855
- (void)windowResignedKey:(NSNotification *)notification
1857
[self sendActivateEvent:NO];
1858
[self setNeedsDisplay:YES];
1859
[self restartNullEvents];
1862
- (void)windowDidMiniaturize:(NSNotification *)notification
1864
[self stopNullEvents];
1867
- (void)windowDidDeminiaturize:(NSNotification *)notification
1869
[self restartNullEvents];
1872
- (void)loginWindowDidSwitchFromUser:(NSNotification *)notification
1874
[self stopNullEvents];
1877
-(void)loginWindowDidSwitchToUser:(NSNotification *)notification
1879
[self restartNullEvents];
1882
- (void)preferencesHaveChanged:(NSNotification *)notification
1884
WebPreferences *preferences = [[self webView] preferences];
1885
BOOL arePlugInsEnabled = [preferences arePlugInsEnabled];
1887
if ([notification object] == preferences && isStarted != arePlugInsEnabled) {
1888
if (arePlugInsEnabled) {
1889
if ([self currentWindow]) {
1894
[self setNeedsDisplay:YES];
1899
- (NPObject *)createPluginScriptableObject
1901
if (!NPP_GetValue || ![self isStarted])
1904
NPObject *value = NULL;
1906
[self willCallPlugInFunction];
1908
KJS::JSLock::DropAllLocks dropAllLocks;
1909
error = NPP_GetValue(plugin, NPPVpluginScriptableNPObject, &value);
1911
[self didCallPlugInFunction];
1912
if (error != NPERR_NO_ERROR)
1918
- (void)willCallPlugInFunction
1922
// Could try to prevent infinite recursion here, but it's probably not worth the effort.
1923
pluginFunctionCallDepth++;
1926
- (void)didCallPlugInFunction
1928
ASSERT(pluginFunctionCallDepth > 0);
1929
pluginFunctionCallDepth--;
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;
1939
-(void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
1941
ASSERT(_loadManually);
1942
ASSERT(!_manualStream);
1944
_manualStream = [[WebNetscapePluginStream alloc] initWithFrameLoader:core([self webFrame])->loader()];
1947
- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
1949
ASSERT(_loadManually);
1950
ASSERT(_manualStream);
1952
_dataLengthReceived += [data length];
1954
if (![self isStarted])
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]];
1964
if ([_manualStream plugin])
1965
[_manualStream receivedData:data];
1968
- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
1970
ASSERT(_loadManually);
1976
if (![self isStarted]) {
1980
[_manualStream destroyStreamWithError:error];
1983
- (void)pluginViewFinishedLoading:(NSView *)pluginView
1985
ASSERT(_loadManually);
1986
ASSERT(_manualStream);
1988
if ([self isStarted])
1989
[_manualStream finishedLoadingWithData:[[self dataSource] data]];
1994
@implementation WebBaseNetscapePluginView (WebNPPCallbacks)
1996
- (NSMutableURLRequest *)requestWithURLCString:(const char *)URLCString
2001
CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, URLCString, kCFStringEncodingISOLatin1);
2002
ASSERT(string); // All strings should be representable in ISO Latin 1
2004
NSString *URLString = [(NSString *)string _web_stringByStrippingReturnCharacters];
2005
NSURL *URL = [NSURL _web_URLWithDataAsString:URLString relativeToURL:baseURL];
2010
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
2011
Frame* frame = core([self webFrame]);
2014
[request _web_setHTTPReferrer:frame->loader()->outgoingReferrer()];
2018
- (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest
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.
2027
NSURL *URL = [[JSPluginRequest request] URL];
2028
NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
2031
NSString *result = [[[self webFrame] _bridge] stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]];
2033
// Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop.
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];
2043
KJS::JSLock::DropAllLocks dropAllLocks;
2044
NPP_URLNotify(plugin, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]);
2046
[self didCallPlugInFunction];
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
2053
notifyData:[JSPluginRequest notifyData]
2054
sendNotification:[JSPluginRequest sendNotification]];
2055
[stream startStreamResponseURL:URL
2056
expectedContentLength:[JSData length]
2057
lastModifiedDate:nil
2058
MIMEType:@"text/plain"
2060
[stream receivedData:JSData];
2061
[stream finishedLoadingWithData:JSData];
2066
- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
2070
WebPluginRequest *pluginRequest = [pendingFrameLoads objectForKey:webFrame];
2071
ASSERT(pluginRequest != nil);
2072
ASSERT([pluginRequest sendNotification]);
2074
[self willCallPlugInFunction];
2076
KJS::JSLock::DropAllLocks dropAllLocks;
2077
NPP_URLNotify(plugin, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]);
2079
[self didCallPlugInFunction];
2081
[pendingFrameLoads removeObjectForKey:webFrame];
2082
[webFrame _setInternalLoadDelegate:nil];
2085
- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
2087
NPReason reason = NPRES_DONE;
2089
reason = [WebBaseNetscapePluginStream reasonForError:error];
2091
[self webFrame:webFrame didFinishLoadWithReason:reason];
2094
- (void)loadPluginRequest:(WebPluginRequest *)pluginRequest
2096
NSURLRequest *request = [pluginRequest request];
2097
NSString *frameName = [pluginRequest frameName];
2098
WebFrame *frame = nil;
2100
NSURL *URL = [request URL];
2101
NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
2103
ASSERT(frameName || JSString);
2106
// FIXME - need to get rid of this window creation which
2107
// bypasses normal targeted link handling
2108
frame = [[self webFrame] findFrameNamed:frameName];
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];
2117
newWebView = [[WebDefaultUIDelegate sharedUIDelegate] webView:currentWebView createWebViewWithRequest:nil];
2121
if ([pluginRequest sendNotification]) {
2122
[self willCallPlugInFunction];
2124
KJS::JSLock::DropAllLocks dropAllLocks;
2125
NPP_URLNotify(plugin, [[[pluginRequest request] URL] _web_URLCString], NPERR_GENERIC_ERROR, [pluginRequest notifyData]);
2127
[self didCallPlugInFunction];
2132
frame = [newWebView mainFrame];
2133
core(frame)->tree()->setName(frameName);
2134
[[newWebView _UIDelegateForwarder] webViewShow:newWebView];
2139
ASSERT(frame == nil || [self webFrame] == frame);
2140
[self evaluateJavaScriptPluginRequest:pluginRequest];
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];
2148
ASSERT([view isKindOfClass:[WebBaseNetscapePluginView class]]);
2149
[view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK];
2151
[pendingFrameLoads _webkit_setObject:pluginRequest forUncopiedKey:frame];
2152
[frame _setInternalLoadDelegate:self];
2157
- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification
2159
NSURL *URL = [request URL];
2162
return NPERR_INVALID_URL;
2164
NSString *target = nil;
2166
// Find the frame given the target string.
2167
target = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, cTarget, kCFStringEncodingWindowsLatin1);
2169
WebFrame *frame = [self webFrame];
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)) {
2177
return NPERR_GENERIC_ERROR;
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;
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.
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.
2199
return NPERR_INVALID_PARAM;
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];
2208
WebNetscapePluginStream *stream = [[WebNetscapePluginStream alloc] initWithRequest:request
2210
notifyData:notifyData
2211
sendNotification:sendNotification];
2213
return NPERR_INVALID_URL;
2215
[streams addObject:stream];
2220
return NPERR_NO_ERROR;
2223
-(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData
2225
LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget);
2227
NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
2228
return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES];
2231
-(NPError)getURL:(const char *)URLCString target:(const char *)cTarget
2233
LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget);
2235
NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
2236
return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO];
2239
- (NPError)_postURL:(const char *)URLCString
2240
target:(const char *)target
2242
buf:(const char *)buf
2244
notifyData:(void *)notifyData
2245
sendNotification:(BOOL)sendNotification
2246
allowHeaders:(BOOL)allowHeaders
2248
if (!URLCString || !len || !buf) {
2249
return NPERR_INVALID_PARAM;
2252
NSData *postData = nil;
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);
2258
return NPERR_INVALID_PARAM;
2260
NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString];
2262
if ([fileURL isFileURL]) {
2263
path = [fileURL path];
2267
postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]];
2268
CFRelease(bufString);
2270
return NPERR_FILE_NOT_FOUND;
2273
postData = [NSData dataWithBytes:buf length:len];
2276
if ([postData length] == 0) {
2277
return NPERR_INVALID_PARAM;
2280
NSMutableURLRequest *request = [self requestWithURLCString:URLCString];
2281
[request setHTTPMethod:@"POST"];
2284
if ([postData _web_startsWithBlankLine]) {
2285
postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)];
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;
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"];
2299
if (contentLength != nil)
2300
dataLength = MIN((unsigned)[contentLength intValue], dataLength);
2301
[header removeObjectForKey:@"Content-Length"];
2303
if ([header count] > 0) {
2304
[request setAllHTTPHeaderFields:header];
2306
// Everything after the blank line is the actual content of the POST.
2307
postData = [postData subdataWithRange:NSMakeRange(location, dataLength)];
2311
if ([postData length] == 0) {
2312
return NPERR_INVALID_PARAM;
2316
// Plug-ins expect to receive uncached data when doing a POST (3347134).
2317
[request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
2318
[request setHTTPBody:postData];
2320
return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification];
2323
- (NPError)postURLNotify:(const char *)URLCString
2324
target:(const char *)target
2326
buf:(const char *)buf
2328
notifyData:(void *)notifyData
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];
2334
-(NPError)postURL:(const char *)URLCString
2335
target:(const char *)target
2337
buf:(const char *)buf
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];
2345
-(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream
2347
LOG(Plugins, "NPN_NewStream");
2348
return NPERR_GENERIC_ERROR;
2351
-(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer
2353
LOG(Plugins, "NPN_Write");
2354
return NPERR_GENERIC_ERROR;
2357
-(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason
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;
2368
WebBaseNetscapePluginStream *browserStream = static_cast<WebBaseNetscapePluginStream *>(stream->ndata);
2369
[browserStream cancelLoadAndDestroyStreamWithError:[browserStream errorForReason:reason]];
2371
return NPERR_NO_ERROR;
2374
- (const char *)userAgent
2376
return [[[self webView] userAgentForURL:baseURL] UTF8String];
2379
-(void)status:(const char *)message
2382
LOG_ERROR("NPN_Status passed a NULL status message");
2386
CFStringRef status = CFStringCreateWithCString(NULL, message, kCFStringEncodingUTF8);
2388
LOG_ERROR("NPN_Status: the message was not valid UTF-8");
2392
LOG(Plugins, "NPN_Status: %@", status);
2393
WebView *wv = [self webView];
2394
[[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status];
2398
-(void)invalidateRect:(NPRect *)invalidRect
2400
LOG(Plugins, "NPN_InvalidateRect");
2401
[self setNeedsDisplayInRect:NSMakeRect(invalidRect->left, invalidRect->top,
2402
(float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)];
2410
- (void)invalidateRegion:(NPRegion)invalidRegion
2412
LOG(Plugins, "NPN_InvalidateRegion");
2413
NSRect invalidRect = NSZeroRect;
2414
switch (drawingModel) {
2415
#ifndef NP_NO_QUICKDRAW
2416
case NPDrawingModelQuickDraw:
2419
GetRegionBounds((NPQDRegion)invalidRegion, &qdRect);
2420
invalidRect = NSMakeRect(qdRect.left, qdRect.top, qdRect.right - qdRect.left, qdRect.bottom - qdRect.top);
2423
#endif /* NP_NO_QUICKDRAW */
2425
case NPDrawingModelCoreGraphics:
2426
case NPDrawingModelOpenGL:
2428
CGRect cgRect = CGPathGetBoundingBox((NPCGRegion)invalidRegion);
2429
invalidRect = *(NSRect *)&cgRect;
2434
ASSERT_NOT_REACHED();
2438
[self setNeedsDisplayInRect:invalidRect];
2443
LOG(Plugins, "forceRedraw");
2444
[self setNeedsDisplay:YES];
2445
[[self window] displayIfNeeded];
2448
- (NPError)getVariable:(NPNVariable)variable value:(void *)value
2451
case NPNVWindowNPObject:
2453
Frame* frame = core([self webFrame]);
2454
NPObject* windowScriptObject = frame ? frame->windowScriptNPObject() : 0;
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);
2460
void **v = (void **)value;
2461
*v = windowScriptObject;
2463
return NPERR_NO_ERROR;
2466
case NPNVPluginElementNPObject:
2469
return NPERR_GENERIC_ERROR;
2471
NPObject *plugInScriptObject = (NPObject *)[element _NPObject];
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);
2477
void **v = (void **)value;
2478
*v = plugInScriptObject;
2480
return NPERR_NO_ERROR;
2483
case NPNVpluginDrawingModel:
2485
*(NPDrawingModel *)value = drawingModel;
2486
return NPERR_NO_ERROR;
2489
#ifndef NP_NO_QUICKDRAW
2490
case NPNVsupportsQuickDrawBool:
2492
*(NPBool *)value = TRUE;
2493
return NPERR_NO_ERROR;
2495
#endif /* NP_NO_QUICKDRAW */
2497
case NPNVsupportsCoreGraphicsBool:
2499
*(NPBool *)value = TRUE;
2500
return NPERR_NO_ERROR;
2503
case NPNVsupportsOpenGLBool:
2505
*(NPBool *)value = TRUE;
2506
return NPERR_NO_ERROR;
2513
return NPERR_GENERIC_ERROR;
2516
- (NPError)setVariable:(NPPVariable)variable value:(void *)value
2519
case NPPVpluginWindowBool:
2521
NPWindowType newWindowType = (value ? NPWindowTypeWindow : NPWindowTypeDrawable);
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];
2527
window.type = newWindowType;
2530
case NPPVpluginTransparentBool:
2532
BOOL newTransparent = (value != 0);
2534
// Redisplay if transparency is changing
2535
if (isTransparent != newTransparent)
2536
[self setNeedsDisplay:YES];
2538
isTransparent = newTransparent;
2540
return NPERR_NO_ERROR;
2543
case NPNVpluginDrawingModel:
2545
// Can only set drawing model inside NPP_New()
2546
if (self != [[self class] currentPluginView])
2547
return NPERR_GENERIC_ERROR;
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:
2556
case NPDrawingModelCoreGraphics:
2557
case NPDrawingModelOpenGL:
2558
drawingModel = newDrawingModel;
2559
return NPERR_NO_ERROR;
2561
// Unsupported (or unknown) drawing models:
2563
LOG(Plugins, "Plugin %@ uses unsupported drawing model: %d", pluginPackage, drawingModel);
2564
return NPERR_GENERIC_ERROR;
2569
return NPERR_GENERIC_ERROR;
2575
@implementation WebPluginRequest
2577
- (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture
2580
_didStartFromUserGesture = currentEventIsUserGesture;
2581
_request = [request retain];
2582
_frameName = [frameName retain];
2583
_notifyData = notifyData;
2584
_sendNotification = sendNotification;
2591
[_frameName release];
2595
- (NSURLRequest *)request
2600
- (NSString *)frameName
2605
- (BOOL)isCurrentEventUserGesture
2607
return _didStartFromUserGesture;
2610
- (BOOL)sendNotification
2612
return _sendNotification;
2615
- (void *)notifyData
2622
@implementation WebBaseNetscapePluginView (Internal)
2624
- (NPError)_createPlugin
2626
plugin = (NPP)calloc(1, sizeof(NPP_t));
2627
plugin->ndata = self;
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);
2634
[[self class] setCurrentPluginView:self];
2635
NPError npErr = NPP_New((char *)[MIMEType cString], plugin, mode, argsCount, cAttributes, cValues, NULL);
2636
[[self class] setCurrentPluginView:nil];
2638
LOG(Plugins, "NPP_New: %d", npErr);
2642
- (void)_destroyPlugin
2645
npErr = NPP_Destroy(plugin, NULL);
2646
LOG(Plugins, "NPP_Destroy: %d", npErr);
2648
if (Frame* frame = core([self webFrame]))
2649
frame->cleanupScriptObjectsForPlugin(self);
2655
- (void)_viewHasMoved
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.
2662
if (drawingModel == NPDrawingModelOpenGL)
2663
[self _reshapeAGLWindow];
2665
#ifndef NP_NO_QUICKDRAW
2666
if (drawingModel == NPDrawingModelQuickDraw)
2667
[self tellQuickTimeToChill];
2669
[self updateAndSetWindow];
2670
[self resetTrackingRect];
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];
2680
- (NSBitmapImageRep *)_printedPluginBitmap
2682
#ifdef NP_NO_QUICKDRAW
2685
// Cannot print plugins that do not implement NPP_Print
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
2698
colorSpaceName:NSDeviceRGBColorSpace
2699
bitmapFormat:NSAlphaFirstBitmapFormat
2701
bitsPerPixel:0] autorelease];
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,
2714
(Ptr)[bitmap bitmapData],
2715
[bitmap bytesPerRow]) != noErr) {
2716
LOG_ERROR("Could not create GWorld for printing");
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
2733
// Create embed-mode NPPrint
2735
npPrint.mode = NP_EMBED;
2736
npPrint.print.embedPrint.window = printNPWindow;
2737
npPrint.print.embedPrint.platformPrint = printGWorld;
2739
// Tell the plugin to print into the GWorld
2740
[self willCallPlugInFunction];
2742
KJS::JSLock::DropAllLocks dropAllLocks;
2743
NPP_Print(plugin, &npPrint);
2745
[self didCallPlugInFunction];
2747
// Don't need the GWorld anymore
2748
DisposeGWorld(printGWorld);
2754
- (BOOL)_createAGLContextIfNeeded
2756
ASSERT(drawingModel == NPDrawingModelOpenGL);
2758
// Do nothing (but indicate success) if the AGL context already exists
2762
switch (window.type) {
2763
case NPWindowTypeWindow:
2764
return [self _createWindowedAGLContext];
2766
case NPWindowTypeDrawable:
2767
return [self _createWindowlessAGLContext];
2770
ASSERT_NOT_REACHED();
2775
- (BOOL)_createWindowedAGLContext
2777
ASSERT(drawingModel == NPDrawingModelOpenGL);
2778
ASSERT(!aglContext);
2780
ASSERT([self window]);
2782
GLint pixelFormatAttributes[] = {
2794
// Choose AGL pixel format
2795
AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL, 0, pixelFormatAttributes);
2797
LOG_ERROR("Could not find suitable AGL pixel format: %s", aglErrorString(aglGetError()));
2801
// Create AGL context
2802
aglContext = aglCreateContext(pixelFormat, NULL);
2803
aglDestroyPixelFormat(pixelFormat);
2805
LOG_ERROR("Could not create AGL context: %s", aglErrorString(aglGetError()));
2809
// Create AGL window
2810
aglWindow = [[NSWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
2812
LOG_ERROR("Could not create window for AGL drawable.");
2816
// AGL window should allow clicks to go through -- mouse events are tracked by WebCore
2817
[aglWindow setIgnoresMouseEvents:YES];
2819
// Make sure the window is not opaque -- windowed plug-ins cannot layer with other page elements
2820
[aglWindow setOpaque:YES];
2822
// Position and order in the AGL window
2823
[self _reshapeAGLWindow];
2825
// Attach the AGL context to its window
2827
#ifdef AGL_VERSION_3_0
2828
success = aglSetWindowRef(aglContext, (WindowRef)[aglWindow windowRef]);
2830
success = aglSetDrawable(aglContext, (AGLDrawable)GetWindowPort((WindowRef)[aglWindow windowRef]));
2833
LOG_ERROR("Could not set AGL drawable: %s", aglErrorString(aglGetError()));
2834
aglDestroyContext(aglContext);
2842
- (BOOL)_createWindowlessAGLContext
2844
ASSERT(drawingModel == NPDrawingModelOpenGL);
2845
ASSERT(!aglContext);
2848
GLint pixelFormatAttributes[] = {
2859
// Choose AGL pixel format
2860
AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL, 0, pixelFormatAttributes);
2862
LOG_ERROR("Could not find suitable AGL pixel format: %s", aglErrorString(aglGetError()));
2866
// Create AGL context
2867
aglContext = aglCreateContext(pixelFormat, NULL);
2868
aglDestroyPixelFormat(pixelFormat);
2870
LOG_ERROR("Could not create AGL context: %s", aglErrorString(aglGetError()));
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);
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);
2888
LOG_ERROR("Could not set offscreen buffer for AGL context: %d", error);
2889
aglDestroyContext(aglContext);
2897
- (CGLContextObj)_cglContext
2899
ASSERT(drawingModel == NPDrawingModelOpenGL);
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()));
2908
- (BOOL)_getAGLOffscreenBuffer:(GLvoid **)outBuffer width:(GLsizei *)outWidth height:(GLsizei *)outHeight
2910
ASSERT(drawingModel == NPDrawingModelOpenGL);
2919
// Only windowless plug-ins have offscreen buffers
2920
if (window.type != NPWindowTypeDrawable)
2923
CGLContextObj cglContext = [self _cglContext];
2927
GLsizei width, height;
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);
2937
*outBuffer = offscreenBuffer;
2941
*outHeight = height;
2946
- (void)_destroyAGLContext
2948
ASSERT(drawingModel == NPDrawingModelOpenGL);
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);
2959
// Detach context from the AGL window
2960
#ifdef AGL_VERSION_3_0
2961
aglSetWindowRef(aglContext, NULL);
2963
aglSetDrawable(aglContext, NULL);
2966
// Destroy the context
2967
aglDestroyContext(aglContext);
2971
// Destroy the AGL window
2973
[self _hideAGLWindow];
2978
- (void)_reshapeAGLWindow
2980
ASSERT(drawingModel == NPDrawingModelOpenGL);
2985
switch (window.type) {
2986
case NPWindowTypeWindow:
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];
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];
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];
3007
// Update the AGL context
3008
aglUpdateContext(aglContext);
3012
case NPWindowTypeDrawable:
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)
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)
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");
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);
3036
LOG_ERROR("Could not set offscreen buffer for AGL context: %d", error);
3040
// Update the AGL context
3041
aglUpdateContext(aglContext);
3046
ASSERT_NOT_REACHED();
3051
- (void)_hideAGLWindow
3053
ASSERT(drawingModel == NPDrawingModelOpenGL);
3058
// aglWindow should only be set for a windowed OpenGL plug-in
3059
ASSERT(window.type == NPWindowTypeWindow);
3061
NSWindow *parentWindow = [aglWindow 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];
3068
[aglWindow orderOut:nil];
3071
- (NSImage *)_aglOffscreenImageForDrawingInRect:(NSRect)drawingInRect
3073
ASSERT(drawingModel == NPDrawingModelOpenGL);
3075
CGLContextObj cglContext = [self _cglContext];
3079
// Get the offscreen buffer
3080
GLvoid *offscreenBuffer;
3081
GLsizei width, height;
3082
if (![self _getAGLOffscreenBuffer:&offscreenBuffer width:&width height:&height])
3085
unsigned char *plane = (unsigned char *)offscreenBuffer;
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)));
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))
3101
#endif /* defined(__i386__) || defined(__x86_64__) */
3103
NSBitmapImageRep *aglBitmap = [[NSBitmapImageRep alloc]
3104
initWithBitmapDataPlanes:&plane
3111
colorSpaceName:NSDeviceRGBColorSpace
3112
bitmapFormat:NSAlphaFirstBitmapFormat
3113
bytesPerRow:width * 4
3116
LOG_ERROR("Could not create bitmap for AGL offscreen buffer");
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];
3128
- (void)_redeliverStream
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]) {
3138
[self pluginView:self receivedError:_error];
3140
[self pluginViewFinishedLoading:self];
3148
@implementation NSData (PluginExtras)
3150
- (BOOL)_web_startsWithBlankLine
3152
return [self length] > 0 && ((const char *)[self bytes])[0] == '\n';
3156
- (NSInteger)_web_locationAfterFirstBlankLine
3158
const char *bytes = (const char *)[self bytes];
3159
unsigned length = [self length];
3162
for (i = 0; i < length - 4; i++) {
3164
// Support for Acrobat. It sends "\n\n".
3165
if (bytes[i] == '\n' && bytes[i+1] == '\n') {
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') {
3174
} else if (bytes[i] == '\n') {
3175
// Support for Director. It sends "\r\n\n" (3880387).
3177
} else if (bytes[i] == '\r' && bytes[i+1] == '\n') {
3178
// Support for Flash. It sends "\r\n\r\n" (3758113).