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