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