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