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