Fix the Mac build with a newer compiler
[WebKit-https.git] / Source / WebKit / mac / Plugins / WebPluginController.mm
1 /*
2  * Copyright (C) 2005, 2006 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
30 #import "WebPluginController.h"
31
32 #import "DOMNodeInternal.h"
33 #import "WebBasePluginPackage.h"
34 #import "WebDataSourceInternal.h"
35 #import "WebFrameInternal.h"
36 #import "WebFrameView.h"
37 #import "WebHTMLViewPrivate.h"
38 #import "WebKitErrorsPrivate.h"
39 #import "WebKitLogging.h"
40 #import "WebNSObjectExtras.h"
41 #import "WebNSURLExtras.h"
42 #import "WebNSViewExtras.h"
43 #import "WebPlugin.h"
44 #import "WebPluginContainer.h"
45 #import "WebPluginContainerCheck.h"
46 #import "WebPluginPackage.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/ResourceRequest.h>
58 #import <WebCore/ScriptController.h>
59 #import <WebCore/WebCoreURLResponse.h>
60 #import <objc/runtime.h>
61 #import <runtime/JSLock.h>
62 #import <wtf/text/WTFString.h>
63
64 #if PLATFORM(IOS)
65 #import "DOMElementInternal.h"
66 #import "WebUIKitDelegate.h"
67 #import <WebCore/AudioSession.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 #if PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < 80000
123 + (id)plugInViewWithArguments:(NSDictionary *)arguments fromPluginPackage:(WebPluginPackage *)pluginPackage
124 {
125     initializeAudioSession();
126     [pluginPackage load];
127     Class viewFactory = [pluginPackage viewFactory];
128
129     id view = nil;
130
131     if ([viewFactory respondsToSelector:@selector(plugInViewWithArguments:)]) {
132         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
133         view = [viewFactory plugInViewWithArguments:arguments];
134     }
135
136     return view;
137 }
138 #else
139 - (NSView *)plugInViewWithArguments:(NSDictionary *)arguments fromPluginPackage:(WebPluginPackage *)pluginPackage
140 {
141 #if PLATFORM(IOS)
142     initializeAudioSession();
143 #endif
144
145     [pluginPackage load];
146
147     NSView *view = nil;
148
149 #if PLATFORM(IOS)
150     {
151         WebView *webView = [_documentView _webView];
152         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
153         view = [[webView _UIKitDelegateForwarder] webView:webView plugInViewWithArguments:arguments fromPlugInPackage:pluginPackage];
154     }
155 #else
156     Class viewFactory = [pluginPackage viewFactory];
157     if ([viewFactory respondsToSelector:@selector(plugInViewWithArguments:)]) {
158         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
159         view = [viewFactory plugInViewWithArguments:arguments];
160     } else if ([viewFactory respondsToSelector:@selector(pluginViewWithArguments:)]) {
161         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
162         view = [viewFactory pluginViewWithArguments:arguments];
163     }
164 #endif
165
166     if (view == nil) {
167         return nil;
168     }
169     
170     if (pluginViews == nil) {
171         pluginViews = [[NSMutableSet alloc] init];
172     }
173     [pluginViews addObject:view];
174
175     return view;
176 }
177 #endif
178
179 #if PLATFORM(IOS)
180 + (void)addPlugInView:(NSView *)view
181 {
182     if (pluginViews == nil)
183         pluginViews = [[NSMutableSet alloc] init];
184
185     ASSERT(view);
186     if (view)
187         [pluginViews addObject:view];
188 }
189 #endif
190
191 + (BOOL)isPlugInView:(NSView *)view
192 {
193     return [pluginViews containsObject:view];
194 }
195
196 - (id)initWithDocumentView:(NSView *)view
197 {
198     self = [super init];
199     if (!self)
200         return nil;
201     _documentView = view;
202     _views = [[NSMutableArray alloc] init];
203     _checksInProgress = (NSMutableSet *)CFMakeCollectable(CFSetCreateMutable(NULL, 0, NULL));
204     return self;
205 }
206
207 - (void)setDataSource:(WebDataSource *)dataSource
208 {
209     _dataSource = dataSource;    
210 }
211
212 - (void)dealloc
213 {
214     [_views release];
215     [_checksInProgress release];
216     [super dealloc];
217 }
218
219 #if PLATFORM(IOS)
220 - (BOOL)plugInsAreRunning
221 {
222     NSUInteger pluginViewCount = [_views count];
223     return _started && pluginViewCount;
224 }
225
226 - (CALayer *)superlayerForPluginView:(NSView *)view
227 {
228     Frame* coreFrame = core([self webFrame]);
229     FrameView* coreView = coreFrame ? coreFrame->view() : nullptr;
230     if (!coreView)
231         return nil;
232
233     // Get a GraphicsLayer;
234     GraphicsLayer* layerForWidget = coreView->graphicsLayerForPlatformWidget(view);
235     if (!layerForWidget)
236         return nil;
237     
238     return layerForWidget->platformLayer();
239 }
240 #endif
241
242 - (void)stopOnePlugin:(NSView *)view
243 {
244     if ([view respondsToSelector:@selector(webPlugInStop)]) {
245         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
246         [view webPlugInStop];
247     } else if ([view respondsToSelector:@selector(pluginStop)]) {
248         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
249         [view pluginStop];
250     }
251 }
252
253 #if PLATFORM(IOS)
254 - (void)stopOnePluginForPageCache:(NSView *)view
255 {
256     if ([view respondsToSelector:@selector(webPlugInStopForPageCache)]) {
257         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
258         [view webPlugInStopForPageCache];
259     } else
260         [self stopOnePlugin:view];
261 }
262 #endif
263
264 - (void)destroyOnePlugin:(NSView *)view
265 {
266     if ([view respondsToSelector:@selector(webPlugInDestroy)]) {
267         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
268         [view webPlugInDestroy];
269     } else if ([view respondsToSelector:@selector(pluginDestroy)]) {
270         JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
271         [view pluginDestroy];
272     }
273 }
274
275 - (void)startAllPlugins
276 {
277     if (_started)
278         return;
279     
280     if ([_views count] > 0)
281         LOG(Plugins, "starting WebKit plugins : %@", [_views description]);
282     
283     int count = [_views count];
284     for (int i = 0; i < count; i++) {
285         id aView = [_views objectAtIndex:i];
286         if ([aView respondsToSelector:@selector(webPlugInStart)]) {
287             JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
288             [aView webPlugInStart];
289         } else if ([aView respondsToSelector:@selector(pluginStart)]) {
290             JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
291             [aView pluginStart];
292         }
293     }
294     _started = YES;
295 }
296
297 - (void)stopAllPlugins
298 {
299     if (!_started)
300         return;
301
302     if ([_views count] > 0) {
303         LOG(Plugins, "stopping WebKit plugins: %@", [_views description]);
304     }
305     
306     int viewsCount = [_views count];
307     for (int i = 0; i < viewsCount; i++)
308         [self stopOnePlugin:[_views objectAtIndex:i]];
309
310     _started = NO;
311 }
312
313 #if PLATFORM(IOS)
314 - (void)stopPluginsForPageCache
315 {
316     if (!_started)
317         return;
318
319     NSUInteger viewsCount = [_views count];
320     if (viewsCount > 0)
321         LOG(Plugins, "stopping WebKit plugins for PageCache: %@", [_views description]);
322
323     for (NSUInteger i = 0; i < viewsCount; ++i)
324         [self stopOnePluginForPageCache:[_views objectAtIndex:i]];
325
326     _started = NO;
327 }
328
329 - (void)restorePluginsFromCache
330 {
331     WebView *webView = [_documentView _webView];
332
333     NSUInteger viewsCount = [_views count];
334     if (viewsCount > 0)
335         LOG(Plugins, "restoring WebKit plugins from PageCache: %@", [_views description]);
336
337     for (NSUInteger i = 0; i < viewsCount; ++i)
338         [[webView _UIKitDelegateForwarder] webView:webView willAddPlugInView:[_views objectAtIndex:i]];
339 }
340 #endif // PLATFORM(IOS)
341
342 - (void)addPlugin:(NSView *)view
343 {
344     if (!_documentView) {
345         LOG_ERROR("can't add a plug-in to a defunct WebPluginController");
346         return;
347     }
348     
349     if (![_views containsObject:view]) {
350         [_views addObject:view];
351 #if !PLATFORM(IOS)
352         [[_documentView _webView] addPluginInstanceView:view];
353 #endif
354
355 #if !PLATFORM(IOS)
356         BOOL oldDefersCallbacks = [[self webView] defersCallbacks];
357         if (!oldDefersCallbacks)
358             [[self webView] setDefersCallbacks:YES];
359
360         if (isKindOfClass(view, @"WmvPlugin"))
361             installFlip4MacPlugInWorkaroundIfNecessary();
362 #endif
363
364         LOG(Plugins, "initializing plug-in %@", view);
365         if ([view respondsToSelector:@selector(webPlugInInitialize)]) {
366             JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
367             [view webPlugInInitialize];
368         } else if ([view respondsToSelector:@selector(pluginInitialize)]) {
369             JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
370             [view pluginInitialize];
371         }
372
373 #if !PLATFORM(IOS)
374         if (!oldDefersCallbacks)
375             [[self webView] setDefersCallbacks:NO];
376 #endif
377         
378         if (_started) {
379             LOG(Plugins, "starting plug-in %@", view);
380             if ([view respondsToSelector:@selector(webPlugInStart)]) {
381                 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
382                 [view webPlugInStart];
383             } else if ([view respondsToSelector:@selector(pluginStart)]) {
384                 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
385                 [view pluginStart];
386             }
387             
388             if ([view respondsToSelector:@selector(setContainingWindow:)]) {
389                 JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
390                 [view setContainingWindow:[_documentView window]];
391             }
392         }
393     }
394 }
395
396 - (void)destroyPlugin:(NSView *)view
397 {
398     if ([_views containsObject:view]) {
399         if (_started)
400             [self stopOnePlugin:view];
401         [self destroyOnePlugin:view];
402
403 #if ENABLE(NETSCAPE_PLUGIN_API)
404         if (Frame* frame = core([self webFrame]))
405             frame->script().cleanupScriptObjectsForPlugin(self);
406 #endif
407         
408         [pluginViews removeObject:view];
409 #if !PLATFORM(IOS)
410         [[_documentView _webView] removePluginInstanceView:view];
411 #endif
412         [_views removeObject:view];
413     }
414 }
415
416 - (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)checkIdentifier
417 {
418     [checkIdentifier cancel];
419     [_checksInProgress removeObject:checkIdentifier];
420 }
421
422 static void cancelOutstandingCheck(const void *item, void *context)
423 {
424     [(id)item cancel];
425 }
426
427 - (void)_cancelOutstandingChecks
428 {
429     if (_checksInProgress) {
430         CFSetApplyFunction((CFSetRef)_checksInProgress, cancelOutstandingCheck, NULL);
431         [_checksInProgress release];
432         _checksInProgress = nil;
433     }
434 }
435
436 - (void)destroyAllPlugins
437 {    
438     [self stopAllPlugins];
439
440     if ([_views count] > 0) {
441         LOG(Plugins, "destroying WebKit plugins: %@", [_views description]);
442     }
443
444     [self _cancelOutstandingChecks];
445     
446     int viewsCount = [_views count];
447     for (int i = 0; i < viewsCount; i++) {
448         id aView = [_views objectAtIndex:i];
449         [self destroyOnePlugin:aView];
450         
451 #if ENABLE(NETSCAPE_PLUGIN_API)
452         if (Frame* frame = core([self webFrame]))
453             frame->script().cleanupScriptObjectsForPlugin(self);
454 #endif
455         
456         [pluginViews removeObject:aView];
457 #if !PLATFORM(IOS)
458         [[_documentView _webView] removePluginInstanceView:aView];
459 #endif
460     }
461
462 #if !PLATFORM(IOS)
463     [_views makeObjectsPerformSelector:@selector(removeFromSuperviewWithoutNeedingDisplay)];
464 #else
465     [_views makeObjectsPerformSelector:@selector(removeFromSuperview)];
466 #endif
467     [_views release];
468     _views = nil;
469
470     _documentView = nil;
471 }
472
473 #if PLATFORM(IOS)
474 - (BOOL)processingUserGesture
475 {
476     return ScriptController::processingUserGesture();
477 }
478 #endif
479
480 - (id)_webPluginContainerCheckIfAllowedToLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target resultObject:(id)obj selector:(SEL)selector
481 {
482     WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:request target:target resultObject:obj selector:selector controller:self contextInfo:nil];
483     [_checksInProgress addObject:check];
484     [check start];
485
486     return check;
487 }
488
489 - (void)webPlugInContainerLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target
490 {
491     if (!request) {
492         LOG_ERROR("nil URL passed");
493         return;
494     }
495     if (!_documentView) {
496         LOG_ERROR("could not load URL %@ because plug-in has already been destroyed", request);
497         return;
498     }
499     WebFrame *frame = [_dataSource webFrame];
500     if (!frame) {
501         LOG_ERROR("could not load URL %@ because plug-in has already been stopped", request);
502         return;
503     }
504     if (!target) {
505         target = @"_top";
506     }
507     NSString *JSString = [[request URL] _webkit_scriptIfJavaScriptURL];
508     if (JSString) {
509         if ([frame findFrameNamed:target] != frame) {
510             LOG_ERROR("JavaScript requests can only be made on the frame that contains the plug-in");
511             return;
512         }
513         [frame _stringByEvaluatingJavaScriptFromString:JSString];
514     } else {
515         if (!request) {
516             LOG_ERROR("could not load URL %@", [request URL]);
517             return;
518         }
519         FrameLoadRequest frameRequest(core(frame), request);
520         frameRequest.setFrameName(target);
521         frameRequest.setShouldCheckNewWindowPolicy(true);
522         core(frame)->loader().load(frameRequest);
523     }
524 }
525
526 #if PLATFORM(IOS)
527 - (void)webPlugInContainerWillShowFullScreenForView:(id)plugInView
528 {
529     WebView *webView = [_dataSource _webView];
530     [[webView _UIKitDelegateForwarder] webView:webView willShowFullScreenForPlugInView:plugInView];
531 }
532
533 - (void)webPlugInContainerDidHideFullScreenForView:(id)plugInView
534 {
535     WebView *webView = [_dataSource _webView];
536     [[webView _UIKitDelegateForwarder] webView:webView didHideFullScreenForPlugInView:plugInView];
537 }
538 #endif
539
540 - (void)webPlugInContainerShowStatus:(NSString *)message
541 {
542     if (!message)
543         message = @"";
544
545     WebView *v = [_dataSource _webView];
546     [[v _UIDelegateForwarder] webView:v setStatusText:message];
547 }
548
549 // For compatibility only.
550 - (void)showStatus:(NSString *)message
551 {
552     [self webPlugInContainerShowStatus:message];
553 }
554
555 #if !PLATFORM(IOS)
556 - (NSColor *)webPlugInContainerSelectionColor
557 {
558     bool primary = true;
559     if (Frame* frame = core([self webFrame]))
560         primary = frame->selection().isFocusedAndActive();
561     return primary ? [NSColor selectedTextBackgroundColor] : [NSColor secondarySelectedControlColor];
562 }
563
564 // For compatibility only.
565 - (NSColor *)selectionColor
566 {
567     return [self webPlugInContainerSelectionColor];
568 }
569 #endif
570
571 - (WebFrame *)webFrame
572 {
573     return [_dataSource webFrame];
574 }
575
576 - (WebView *)webView
577 {
578     return [[self webFrame] webView];
579 }
580
581 - (NSString *)URLPolicyCheckReferrer
582 {
583     NSURL *responseURL = [[[[self webFrame] _dataSource] response] URL];
584     ASSERT(responseURL);
585     return [responseURL _web_originalDataAsString];
586 }
587
588 - (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
589 {    
590     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveResponse:)])
591         [pluginView webPlugInMainResourceDidReceiveResponse:response];
592     else {
593         // Cancel the load since this plug-in does its own loading.
594         // FIXME: See <rdar://problem/4258008> for a problem with this.
595         NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad
596                                                         contentURL:[response URL]
597                                                      pluginPageURL:nil
598                                                         pluginName:nil // FIXME: Get this from somewhere
599                                                           MIMEType:[response MIMEType]];
600         [_dataSource _documentLoader]->cancelMainResourceLoad(error);
601         [error release];
602     }        
603 }
604
605 - (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
606 {
607     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveData:)])
608         [pluginView webPlugInMainResourceDidReceiveData:data];
609 }
610
611 - (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
612 {
613     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFailWithError:)])
614         [pluginView webPlugInMainResourceDidFailWithError:error];
615 }
616
617 - (void)pluginViewFinishedLoading:(NSView *)pluginView
618 {
619     if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFinishLoading)])
620         [pluginView webPlugInMainResourceDidFinishLoading];
621 }
622
623 @end
624
625 #if !PLATFORM(IOS)
626 static bool isKindOfClass(id object, NSString *className)
627 {
628     Class cls = NSClassFromString(className);
629
630     if (!cls)
631         return false;
632
633     return [object isKindOfClass:cls];
634 }
635
636
637 // Existing versions of the Flip4Mac WebKit plug-in have an object lifetime bug related to an NSAlert that is
638 // used to notify the user about updates to the plug-in. This bug can result in Safari crashing if the page
639 // containing the plug-in navigates while the alert is displayed (<rdar://problem/7313430>).
640 //
641 // The gist of the bug is thus: Flip4Mac sets an instance of the TSUpdateCheck class as the modal delegate of the
642 // NSAlert instance. This TSUpdateCheck instance itself has a delegate. The delegate is set to the WmvPlugin
643 // instance which is the NSView subclass that is exposed to WebKit as the plug-in view. Since this relationship
644 // is that of delegates the TSUpdateCheck does not retain the WmvPlugin. This leads to a bug if the WmvPlugin
645 // instance is destroyed before the TSUpdateCheck instance as the TSUpdateCheck instance will be left with a
646 // pointer to a stale object. This will happen if a page containing the Flip4Mac plug-in triggers a navigation
647 // while the update sheet is visible as the WmvPlugin instance is removed from the view hierarchy and there are
648 // no other references to keep the object alive.
649 //
650 // We work around this bug by patching the following two messages:
651 //
652 // 1) -[NSAlert beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:]
653 // 2) -[TSUpdateCheck alertDidEnd:returnCode:contextInfo:]
654 //
655 // Our override of 1) detects whether it is Flip4Mac's update sheet triggering the alert by checking whether the
656 // modal delegate is an instance of TSUpdateCheck. If it is, it retains the modal delegate's delegate.
657 //
658 // Our override of 2) then autoreleases the delegate, balancing the retain we added in 1).
659 //
660 // These two overrides have the effect of ensuring that the WmvPlugin instance will always outlive the TSUpdateCheck
661 // instance, preventing the TSUpdateCheck instance from accessing a stale delegate pointer and crashing the application.
662
663
664 typedef void (*beginSheetModalForWindowIMP)(id, SEL, NSWindow *, id, SEL, void*);
665 static beginSheetModalForWindowIMP original_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_;
666
667 typedef void (*alertDidEndIMP)(id, SEL, NSAlert *, NSInteger, void*);
668 static alertDidEndIMP original_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_;
669
670 @class TSUpdateCheck;
671
672 static void WebKit_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_(id object, SEL selector, NSAlert *alert, NSInteger returnCode, void* contextInfo)
673 {
674     [[(TSUpdateCheck *)object delegate] autorelease];
675
676     original_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_(object, selector, alert, returnCode, contextInfo);
677 }
678
679 static void WebKit_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(id object, SEL selector, NSWindow *window, id modalDelegate, SEL didEndSelector, void* contextInfo)
680 {
681     if (isKindOfClass(modalDelegate, @"TSUpdateCheck"))
682         [[(TSUpdateCheck *)modalDelegate delegate] retain];
683
684     original_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(object, selector, window, modalDelegate, didEndSelector, contextInfo);
685 }
686
687 static void installFlip4MacPlugInWorkaroundIfNecessary()
688 {
689     static bool hasInstalledFlip4MacPlugInWorkaround;
690     if (!hasInstalledFlip4MacPlugInWorkaround) {
691         Class TSUpdateCheck = objc_lookUpClass("TSUpdateCheck");
692         if (!TSUpdateCheck)
693             return;
694
695         Method methodToPatch = class_getInstanceMethod(TSUpdateCheck, @selector(alertDidEnd:returnCode:contextInfo:));
696         if (!methodToPatch)
697             return;
698
699         IMP originalMethod = method_setImplementation(methodToPatch, reinterpret_cast<IMP>(WebKit_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_));
700         original_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_ = reinterpret_cast<alertDidEndIMP>(originalMethod);
701
702         methodToPatch = class_getInstanceMethod(objc_getRequiredClass("NSAlert"), @selector(beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:));
703         originalMethod = method_setImplementation(methodToPatch, reinterpret_cast<IMP>(WebKit_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_));
704         original_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_ = reinterpret_cast<beginSheetModalForWindowIMP>(originalMethod);
705
706         hasInstalledFlip4MacPlugInWorkaround = true;
707     }
708 }
709 #endif // !PLATFORM(IOS)