[iOS] Upstream PLATFORM(IOS) changes to Source/WebKit/
[WebKit-https.git] / Source / WebKit / mac / Plugins / WebPluginController.mm
1 /*
2  * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29
30 #import "WebPluginController.h"
31
32 #import "DOMNodeInternal.h"
33 #import "WebDataSourceInternal.h"
34 #import "WebFrameInternal.h"
35 #import "WebFrameView.h"
36 #import "WebHTMLViewPrivate.h"
37 #import "WebKitErrorsPrivate.h"
38 #import "WebKitLogging.h"
39 #import "WebNSObjectExtras.h"
40 #import "WebNSURLExtras.h"
41 #import "WebNSViewExtras.h"
42 #import "WebPlugin.h"
43 #import "WebPluginContainer.h"
44 #import "WebPluginContainerCheck.h"
45 #import "WebPluginPackage.h"
46 #import "WebPluginPrivate.h"
47 #import "WebPluginViewFactory.h"
48 #import "WebUIDelegate.h"
49 #import "WebViewInternal.h"
50 #import <Foundation/NSURLRequest.h>
51 #import <WebCore/DocumentLoader.h>
52 #import <WebCore/Frame.h>
53 #import <WebCore/FrameLoadRequest.h>
54 #import <WebCore/FrameLoader.h>
55 #import <WebCore/HTMLMediaElement.h>
56 #import <WebCore/HTMLNames.h>
57 #import <WebCore/MediaPlayerProxy.h>
58 #import <WebCore/ResourceRequest.h>
59 #import <WebCore/ScriptController.h>
60 #import <WebCore/WebCoreURLResponse.h>
61 #import <objc/runtime.h>
62 #import <runtime/JSLock.h>
63 #import <wtf/text/WTFString.h>
64
65 #if PLATFORM(IOS)
66 #import "DOMElementInternal.h"
67 #import "WebUIKitDelegate.h"
68 #import <WebCore/FrameView.h>
69 #import <WebCore/GraphicsLayer.h>
70 #import <WebCore/Page.h>
71 #import <WebCore/RuntimeApplicationChecksIOS.h>
72 #import <WebCore/SoftLinking.h>
73 #import <WebCore/WebCoreThreadRun.h>
74 #endif
75
76 using namespace WebCore;
77 using namespace HTMLNames;
78
79 @interface NSView (PluginSecrets)
80 - (void)setContainingWindow:(NSWindow *)w;
81 @end
82
83 #if !PLATFORM(IOS)
84 // For compatibility only.
85 @interface NSObject (OldPluginAPI)
86 + (NSView *)pluginViewWithArguments:(NSDictionary *)arguments;
87 @end
88 #endif
89
90 @interface NSView (OldPluginAPI)
91 - (void)pluginInitialize;
92 - (void)pluginStart;
93 - (void)pluginStop;
94 - (void)pluginDestroy;
95 @end
96
97 #if !PLATFORM(IOS)
98 static bool isKindOfClass(id, NSString* className);
99 static void installFlip4MacPlugInWorkaroundIfNecessary();
100 #endif
101
102
103 static NSMutableSet *pluginViews = nil;
104
105 #if PLATFORM(IOS)
106 static void initializeAudioSession()
107 {
108     static bool wasAudioSessionInitialized;
109     if (wasAudioSessionInitialized)
110         return;
111
112     wasAudioSessionInitialized = true;
113     if (!WebCore::applicationIsMobileSafari())
114         return;
115
116     AudioSession::sharedSession().setCategory(AudioSession::MediaPlayback);
117 }
118 #endif
119
120 @implementation WebPluginController
121
122 + (NSView *)plugInViewWithArguments:(NSDictionary *)arguments fromPluginPackage:(WebPluginPackage *)pluginPackage
123 {
124 #if PLATFORM(IOS)
125     initializeAudioSession();
126 #endif
127
128     [pluginPackage load];
129     Class viewFactory = [pluginPackage viewFactory];
130     
131     NSView *view = nil;
132
133 #if PLATFORM(IOS)
134     {
135         WebView *webView = [_documentView _webView];
136         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
137         view = [[webView _UIKitDelegateForwarder] webView:webView plugInViewWithArguments:arguments fromPlugInPackage:pluginPackage];
138     }
139 #else
140     if ([viewFactory respondsToSelector:@selector(plugInViewWithArguments:)]) {
141         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
142         view = [viewFactory plugInViewWithArguments:arguments];
143     } else if ([viewFactory respondsToSelector:@selector(pluginViewWithArguments:)]) {
144         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
145         view = [viewFactory pluginViewWithArguments:arguments];
146     }
147 #endif
148     
149     if (view == nil) {
150         return nil;
151     }
152     
153     if (pluginViews == nil) {
154         pluginViews = [[NSMutableSet alloc] init];
155     }
156     [pluginViews addObject:view];
157     
158     return view;
159 }
160
161 #if PLATFORM(IOS)
162 + (void)addPlugInView:(NSView *)view
163 {
164     if (pluginViews == nil)
165         pluginViews = [[NSMutableSet alloc] init];
166     [pluginViews addObject:view];
167 }
168 #endif
169
170 + (BOOL)isPlugInView:(NSView *)view
171 {
172     return [pluginViews containsObject:view];
173 }
174
175 - (id)initWithDocumentView:(NSView *)view
176 {
177     self = [super init];
178     if (!self)
179         return nil;
180     _documentView = view;
181     _views = [[NSMutableArray alloc] init];
182     _checksInProgress = (NSMutableSet *)CFMakeCollectable(CFSetCreateMutable(NULL, 0, NULL));
183     return self;
184 }
185
186 - (void)setDataSource:(WebDataSource *)dataSource
187 {
188     _dataSource = dataSource;    
189 }
190
191 - (void)dealloc
192 {
193     [_views release];
194     [_checksInProgress release];
195 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
196     [_viewsNotInDocument release];
197 #endif
198     [super dealloc];
199 }
200
201 #if PLATFORM(IOS)
202 - (BOOL)plugInsAreRunning
203 {
204     NSUInteger pluginViewCount = [_views count];
205 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
206     pluginViewCount += [_viewsNotInDocument count];
207 #endif
208     return _started && pluginViewCount;
209 }
210
211 - (CALayer *)superlayerForPluginView:(NSView *)view
212 {
213 #if USE(ACCELERATED_COMPOSITING)
214     Frame* coreFrame = core([self webFrame]);
215     FrameView* coreView = coreFrame ? coreFrame->view() : nullptr;
216     if (!coreView)
217         return nil;
218
219     // Get a GraphicsLayer;
220     GraphicsLayer* layerForWidget = coreView->graphicsLayerForPlatformWidget(view);
221     if (!layerForWidget)
222         return nil;
223     
224     return layerForWidget->platformLayer();
225 #else
226     return nil;
227 #endif
228 }
229 #endif
230
231 - (void)stopOnePlugin:(NSView *)view
232 {
233     if ([view respondsToSelector:@selector(webPlugInStop)]) {
234         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
235         [view webPlugInStop];
236     } else if ([view respondsToSelector:@selector(pluginStop)]) {
237         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
238         [view pluginStop];
239     }
240 }
241
242 #if PLATFORM(IOS)
243 - (void)stopOnePluginForPageCache:(NSView *)view
244 {
245     if ([view respondsToSelector:@selector(webPlugInStopForPageCache)]) {
246         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
247         [view webPlugInStopForPageCache];
248     } else
249         [self stopOnePlugin:view];
250 }
251 #endif
252
253 - (void)destroyOnePlugin:(NSView *)view
254 {
255     if ([view respondsToSelector:@selector(webPlugInDestroy)]) {
256         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
257         [view webPlugInDestroy];
258     } else if ([view respondsToSelector:@selector(pluginDestroy)]) {
259         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
260         [view pluginDestroy];
261     }
262 }
263
264 - (void)startAllPlugins
265 {
266     if (_started)
267         return;
268     
269     if ([_views count] > 0)
270         LOG(Plugins, "starting WebKit plugins : %@", [_views description]);
271     
272     int count = [_views count];
273     for (int i = 0; i < count; i++) {
274         id aView = [_views objectAtIndex:i];
275         if ([aView respondsToSelector:@selector(webPlugInStart)]) {
276             JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
277             [aView webPlugInStart];
278         } else if ([aView respondsToSelector:@selector(pluginStart)]) {
279             JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
280             [aView pluginStart];
281         }
282     }
283     _started = YES;
284 }
285
286 - (void)stopAllPlugins
287 {
288     if (!_started)
289         return;
290
291     if ([_views count] > 0) {
292         LOG(Plugins, "stopping WebKit plugins: %@", [_views description]);
293     }
294     
295     int viewsCount = [_views count];
296     for (int i = 0; i < viewsCount; i++)
297         [self stopOnePlugin:[_views objectAtIndex:i]];
298
299 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
300     int viewsNotInDocumentCount = [_viewsNotInDocument count];
301     for (int i = 0; i < viewsNotInDocumentCount; i++)
302         [self stopOnePlugin:[_viewsNotInDocument objectAtIndex:i]];
303 #endif
304
305     _started = NO;
306 }
307
308 #if PLATFORM(IOS)
309 - (void)stopPluginsForPageCache
310 {
311     if (!_started)
312         return;
313
314     NSUInteger viewsCount = [_views count];
315     if (viewsCount > 0)
316         LOG(Plugins, "stopping WebKit plugins for PageCache: %@", [_views description]);
317
318     for (NSUInteger i = 0; i < viewsCount; ++i)
319         [self stopOnePluginForPageCache:[_views objectAtIndex:i]];
320
321 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
322     NSUInteger viewsNotInDocumentCount = [_viewsNotInDocument count];
323     for (NSUInteger i = 0; i < viewsNotInDocumentCount; ++i)
324         [self stopOnePluginForPageCache:[_viewsNotInDocument objectAtIndex:i]];
325 #endif
326
327     _started = NO;
328 }
329
330 - (void)restorePluginsFromCache
331 {
332     WebView *webView = [_documentView _webView];
333
334     NSUInteger viewsCount = [_views count];
335     if (viewsCount > 0)
336         LOG(Plugins, "restoring WebKit plugins from PageCache: %@", [_views description]);
337
338     for (NSUInteger i = 0; i < viewsCount; ++i)
339         [[webView _UIKitDelegateForwarder] webView:webView willAddPlugInView:[_views objectAtIndex:i]];
340
341 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
342     NSUInteger viewsNotInDocumentCount = [_viewsNotInDocument count];
343     for (NSUInteger i = 0; i < viewsNotInDocumentCount; ++i)
344         [[webView _UIKitDelegateForwarder] webView:webView willAddPlugInView:[_viewsNotInDocument objectAtIndex:i]];
345 #endif
346 }
347 #endif // PLATFORM(IOS)
348
349 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
350 - (void)pluginViewCreated:(NSView *)view
351 {
352     if (!_viewsNotInDocument)
353         _viewsNotInDocument= [[NSMutableArray alloc] init];
354     if (![_viewsNotInDocument containsObject:view])
355         [_viewsNotInDocument addObject:view];
356 }
357
358 + (void)pluginViewHidden:(NSView *)view
359 {
360     [pluginViews removeObject:view];
361 }
362 #endif
363
364 - (void)addPlugin:(NSView *)view
365 {
366     if (!_documentView) {
367         LOG_ERROR("can't add a plug-in to a defunct WebPluginController");
368         return;
369     }
370     
371     if (![_views containsObject:view]) {
372         [_views addObject:view];
373 #if !PLATFORM(IOS)
374         [[_documentView _webView] addPluginInstanceView:view];
375 #endif
376
377 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
378         if ([_viewsNotInDocument containsObject:view])
379             [_viewsNotInDocument removeObject:view];
380 #endif
381
382 #if !PLATFORM(IOS)
383         BOOL oldDefersCallbacks = [[self webView] defersCallbacks];
384         if (!oldDefersCallbacks)
385             [[self webView] setDefersCallbacks:YES];
386
387         if (isKindOfClass(view, @"WmvPlugin"))
388             installFlip4MacPlugInWorkaroundIfNecessary();
389 #endif
390
391         LOG(Plugins, "initializing plug-in %@", view);
392         if ([view respondsToSelector:@selector(webPlugInInitialize)]) {
393             JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
394             [view webPlugInInitialize];
395         } else if ([view respondsToSelector:@selector(pluginInitialize)]) {
396             JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
397             [view pluginInitialize];
398         }
399
400 #if !PLATFORM(IOS)
401         if (!oldDefersCallbacks)
402             [[self webView] setDefersCallbacks:NO];
403 #endif
404         
405         if (_started) {
406             LOG(Plugins, "starting plug-in %@", view);
407             if ([view respondsToSelector:@selector(webPlugInStart)]) {
408                 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
409                 [view webPlugInStart];
410             } else if ([view respondsToSelector:@selector(pluginStart)]) {
411                 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
412                 [view pluginStart];
413             }
414             
415             if ([view respondsToSelector:@selector(setContainingWindow:)]) {
416                 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
417                 [view setContainingWindow:[_documentView window]];
418             }
419         }
420     }
421 }
422
423 - (void)destroyPlugin:(NSView *)view
424 {
425 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
426     if ([_views containsObject:view] || [_viewsNotInDocument containsObject:view]) {
427 #else
428     if ([_views containsObject:view]) {
429 #endif
430         if (_started)
431             [self stopOnePlugin:view];
432         [self destroyOnePlugin:view];
433         
434 #if ENABLE(NETSCAPE_PLUGIN_API)
435         if (Frame* frame = core([self webFrame]))
436             frame->script().cleanupScriptObjectsForPlugin(self);
437 #endif
438         
439         [pluginViews removeObject:view];
440 #if !PLATFORM(IOS)
441         [[_documentView _webView] removePluginInstanceView:view];
442 #endif
443         [_views removeObject:view];
444 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
445         [_viewsNotInDocument removeObject:view];
446 #endif
447     }
448 }
449
450 - (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)checkIdentifier
451 {
452     [checkIdentifier cancel];
453     [_checksInProgress removeObject:checkIdentifier];
454 }
455
456 static void cancelOutstandingCheck(const void *item, void *context)
457 {
458     [(id)item cancel];
459 }
460
461 - (void)_cancelOutstandingChecks
462 {
463     if (_checksInProgress) {
464         CFSetApplyFunction((CFSetRef)_checksInProgress, cancelOutstandingCheck, NULL);
465         [_checksInProgress release];
466         _checksInProgress = nil;
467     }
468 }
469
470 - (void)destroyAllPlugins
471 {    
472     [self stopAllPlugins];
473
474     if ([_views count] > 0) {
475         LOG(Plugins, "destroying WebKit plugins: %@", [_views description]);
476     }
477
478     [self _cancelOutstandingChecks];
479     
480     int viewsCount = [_views count];
481     for (int i = 0; i < viewsCount; i++) {
482         id aView = [_views objectAtIndex:i];
483         [self destroyOnePlugin:aView];
484         
485 #if ENABLE(NETSCAPE_PLUGIN_API)
486         if (Frame* frame = core([self webFrame]))
487             frame->script().cleanupScriptObjectsForPlugin(self);
488 #endif
489         
490         [pluginViews removeObject:aView];
491 #if !PLATFORM(IOS)
492         [[_documentView _webView] removePluginInstanceView:aView];
493 #endif
494     }
495
496 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
497     int viewsNotInDocumentCount = [_viewsNotInDocument count];
498     for (int i = 0; i < viewsNotInDocumentCount; i++)
499         [self destroyOnePlugin:[_viewsNotInDocument objectAtIndex:i]];
500 #endif
501
502 #if !PLATFORM(IOS)
503     [_views makeObjectsPerformSelector:@selector(removeFromSuperviewWithoutNeedingDisplay)];
504 #else
505     [_views makeObjectsPerformSelector:@selector(removeFromSuperview)];
506 #endif
507     [_views release];
508     _views = nil;
509
510     _documentView = nil;
511 }
512
513 #if PLATFORM(IOS)
514 - (BOOL)processingUserGesture
515 {
516     return ScriptController::processingUserGesture();
517 }
518 #endif
519
520 - (id)_webPluginContainerCheckIfAllowedToLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target resultObject:(id)obj selector:(SEL)selector
521 {
522     WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:request target:target resultObject:obj selector:selector controller:self contextInfo:nil];
523     [_checksInProgress addObject:check];
524     [check start];
525
526     return check;
527 }
528
529 - (void)webPlugInContainerLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target
530 {
531     if (!request) {
532         LOG_ERROR("nil URL passed");
533         return;
534     }
535     if (!_documentView) {
536         LOG_ERROR("could not load URL %@ because plug-in has already been destroyed", request);
537         return;
538     }
539     WebFrame *frame = [_dataSource webFrame];
540     if (!frame) {
541         LOG_ERROR("could not load URL %@ because plug-in has already been stopped", request);
542         return;
543     }
544     if (!target) {
545         target = @"_top";
546     }
547     NSString *JSString = [[request URL] _webkit_scriptIfJavaScriptURL];
548     if (JSString) {
549         if ([frame findFrameNamed:target] != frame) {
550             LOG_ERROR("JavaScript requests can only be made on the frame that contains the plug-in");
551             return;
552         }
553         [frame _stringByEvaluatingJavaScriptFromString:JSString];
554     } else {
555         if (!request) {
556             LOG_ERROR("could not load URL %@", [request URL]);
557             return;
558         }
559         FrameLoadRequest frameRequest(core(frame), request);
560         frameRequest.setFrameName(target);
561         frameRequest.setShouldCheckNewWindowPolicy(true);
562         core(frame)->loader().load(frameRequest);
563     }
564 }
565
566 #if PLATFORM(IOS)
567 - (void)webPlugInContainerWillShowFullScreenForView:(id)plugInView
568 {
569     WebView *webView = [_dataSource _webView];
570     [[webView _UIKitDelegateForwarder] webView:webView willShowFullScreenForPlugInView:plugInView];
571 }
572
573 - (void)webPlugInContainerDidHideFullScreenForView:(id)plugInView
574 {
575     WebView *webView = [_dataSource _webView];
576     [[webView _UIKitDelegateForwarder] webView:webView didHideFullScreenForPlugInView:plugInView];
577 }
578 #endif
579
580 - (void)webPlugInContainerShowStatus:(NSString *)message
581 {
582     if (!message)
583         message = @"";
584
585     WebView *v = [_dataSource _webView];
586     [[v _UIDelegateForwarder] webView:v setStatusText:message];
587 }
588
589 // For compatibility only.
590 - (void)showStatus:(NSString *)message
591 {
592     [self webPlugInContainerShowStatus:message];
593 }
594
595 #if !PLATFORM(IOS)
596 - (NSColor *)webPlugInContainerSelectionColor
597 {
598     bool primary = true;
599     if (Frame* frame = core([self webFrame]))
600         primary = frame->selection().isFocusedAndActive();
601     return primary ? [NSColor selectedTextBackgroundColor] : [NSColor secondarySelectedControlColor];
602 }
603
604 // For compatibility only.
605 - (NSColor *)selectionColor
606 {
607     return [self webPlugInContainerSelectionColor];
608 }
609 #endif
610
611 - (WebFrame *)webFrame
612 {
613     return [_dataSource webFrame];
614 }
615
616 - (WebView *)webView
617 {
618     return [[self webFrame] webView];
619 }
620
621 - (NSString *)URLPolicyCheckReferrer
622 {
623     NSURL *responseURL = [[[[self webFrame] _dataSource] response] URL];
624     ASSERT(responseURL);
625     return [responseURL _web_originalDataAsString];
626 }
627
628 - (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
629 {    
630     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveResponse:)])
631         [pluginView webPlugInMainResourceDidReceiveResponse:response];
632     else {
633         // Cancel the load since this plug-in does its own loading.
634         // FIXME: See <rdar://problem/4258008> for a problem with this.
635         NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad
636                                                         contentURL:[response URL]
637                                                      pluginPageURL:nil
638                                                         pluginName:nil // FIXME: Get this from somewhere
639                                                           MIMEType:[response MIMEType]];
640         [_dataSource _documentLoader]->cancelMainResourceLoad(error);
641         [error release];
642     }        
643 }
644
645 - (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
646 {
647     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveData:)])
648         [pluginView webPlugInMainResourceDidReceiveData:data];
649 }
650
651 - (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
652 {
653     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFailWithError:)])
654         [pluginView webPlugInMainResourceDidFailWithError:error];
655 }
656
657 - (void)pluginViewFinishedLoading:(NSView *)pluginView
658 {
659     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFinishLoading)])
660         [pluginView webPlugInMainResourceDidFinishLoading];
661 }
662
663 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
664 static WebCore::HTMLMediaElement* mediaProxyClient(DOMElement* element)
665 {
666     if (!element) {
667         LOG_ERROR("nil element passed");
668         return nil;
669     }
670
671     Element* node = core(element);
672     if (!node || (!isHTMLVideoElement(node) && !isHTMLAudioElement(node))) {
673         LOG_ERROR("invalid media element passed");
674         return nil;
675     }
676
677     return static_cast<WebCore::HTMLMediaElement*>(node);
678 }
679
680 - (void)_webPluginContainerSetMediaPlayerProxy:(WebMediaPlayerProxy *)proxy forElement:(DOMElement *)element
681 {
682 #if PLATFORM(IOS)
683     WebThreadRun(^{
684 #endif
685     WebCore::HTMLMediaElement* client = mediaProxyClient(element);
686     if (client)
687         client->setMediaPlayerProxy(proxy);
688 #if PLATFORM(IOS)
689     });
690 #endif
691 }
692
693 - (void)_webPluginContainerPostMediaPlayerNotification:(int)notification forElement:(DOMElement *)element
694 {
695 #if PLATFORM(IOS)
696     WebThreadRun(^{
697 #endif
698     WebCore::HTMLMediaElement* client = mediaProxyClient(element);
699     if (client)
700         client->deliverNotification((MediaPlayerProxyNotificationType)notification);
701 #if PLATFORM(IOS)
702     });
703 #endif
704 }
705 #endif
706
707 @end
708
709 #if !PLATFORM(IOS)
710 static bool isKindOfClass(id object, NSString *className)
711 {
712     Class cls = NSClassFromString(className);
713
714     if (!cls)
715         return false;
716
717     return [object isKindOfClass:cls];
718 }
719
720
721 // Existing versions of the Flip4Mac WebKit plug-in have an object lifetime bug related to an NSAlert that is
722 // used to notify the user about updates to the plug-in. This bug can result in Safari crashing if the page
723 // containing the plug-in navigates while the alert is displayed (<rdar://problem/7313430>).
724 //
725 // The gist of the bug is thus: Flip4Mac sets an instance of the TSUpdateCheck class as the modal delegate of the
726 // NSAlert instance. This TSUpdateCheck instance itself has a delegate. The delegate is set to the WmvPlugin
727 // instance which is the NSView subclass that is exposed to WebKit as the plug-in view. Since this relationship
728 // is that of delegates the TSUpdateCheck does not retain the WmvPlugin. This leads to a bug if the WmvPlugin
729 // instance is destroyed before the TSUpdateCheck instance as the TSUpdateCheck instance will be left with a
730 // pointer to a stale object. This will happen if a page containing the Flip4Mac plug-in triggers a navigation
731 // while the update sheet is visible as the WmvPlugin instance is removed from the view hierarchy and there are
732 // no other references to keep the object alive.
733 //
734 // We work around this bug by patching the following two messages:
735 //
736 // 1) -[NSAlert beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:]
737 // 2) -[TSUpdateCheck alertDidEnd:returnCode:contextInfo:]
738 //
739 // Our override of 1) detects whether it is Flip4Mac's update sheet triggering the alert by checking whether the
740 // modal delegate is an instance of TSUpdateCheck. If it is, it retains the modal delegate's delegate.
741 //
742 // Our override of 2) then autoreleases the delegate, balancing the retain we added in 1).
743 //
744 // These two overrides have the effect of ensuring that the WmvPlugin instance will always outlive the TSUpdateCheck
745 // instance, preventing the TSUpdateCheck instance from accessing a stale delegate pointer and crashing the application.
746
747
748 typedef void (*beginSheetModalForWindowIMP)(id, SEL, NSWindow *, id, SEL, void*);
749 static beginSheetModalForWindowIMP original_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_;
750
751 typedef void (*alertDidEndIMP)(id, SEL, NSAlert *, NSInteger, void*);
752 static alertDidEndIMP original_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_;
753
754 static void WebKit_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_(id object, SEL selector, NSAlert *alert, NSInteger returnCode, void* contextInfo)
755 {
756     [[object delegate] autorelease];
757
758     original_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_(object, selector, alert, returnCode, contextInfo);
759 }
760
761 static void WebKit_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(id object, SEL selector, NSWindow *window, id modalDelegate, SEL didEndSelector, void* contextInfo)
762 {
763     if (isKindOfClass(modalDelegate, @"TSUpdateCheck"))
764         [[modalDelegate delegate] retain];
765
766     original_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(object, selector, window, modalDelegate, didEndSelector, contextInfo);
767 }
768
769 static void installFlip4MacPlugInWorkaroundIfNecessary()
770 {
771     static bool hasInstalledFlip4MacPlugInWorkaround;
772     if (!hasInstalledFlip4MacPlugInWorkaround) {
773         Class TSUpdateCheck = objc_lookUpClass("TSUpdateCheck");
774         if (!TSUpdateCheck)
775             return;
776
777         Method methodToPatch = class_getInstanceMethod(TSUpdateCheck, @selector(alertDidEnd:returnCode:contextInfo:));
778         if (!methodToPatch)
779             return;
780
781         IMP originalMethod = method_setImplementation(methodToPatch, reinterpret_cast<IMP>(WebKit_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_));
782         original_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_ = reinterpret_cast<alertDidEndIMP>(originalMethod);
783
784         methodToPatch = class_getInstanceMethod(objc_getRequiredClass("NSAlert"), @selector(beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:));
785         originalMethod = method_setImplementation(methodToPatch, reinterpret_cast<IMP>(WebKit_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_));
786         original_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_ = reinterpret_cast<beginSheetModalForWindowIMP>(originalMethod);
787
788         hasInstalledFlip4MacPlugInWorkaround = true;
789     }
790 }
791 #endif // !PLATFORM(IOS)