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