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