1/* 2 * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 30#import "WebPluginController.h" 31 32#import "DOMNodeInternal.h" 33#import "WebDataSourceInternal.h" 34#import "WebFrameInternal.h" 35#import "WebFrameView.h" 36#import "WebHTMLViewPrivate.h" 37#import "WebKitErrorsPrivate.h" 38#import "WebKitLogging.h" 39#import "WebNSURLExtras.h" 40#import "WebNSViewExtras.h" 41#import "WebPlugin.h" 42#import "WebPluginContainer.h" 43#import "WebPluginContainerCheck.h" 44#import "WebPluginPackage.h" 45#import "WebPluginPrivate.h" 46#import "WebPluginViewFactory.h" 47#import "WebUIDelegate.h" 48#import "WebViewInternal.h" 49#import <Foundation/NSURLRequest.h> 50#import <WebCore/DocumentLoader.h> 51#import <WebCore/Frame.h> 52#import <WebCore/FrameLoader.h> 53#import <WebCore/HTMLMediaElement.h> 54#import <WebCore/HTMLNames.h> 55#import <WebCore/MediaPlayerProxy.h> 56#import <WebCore/PlatformString.h> 57#import <WebCore/ResourceRequest.h> 58#import <WebCore/ScriptController.h> 59#import <WebCore/WebCoreURLResponse.h> 60#import <runtime/JSLock.h> 61 62using namespace WebCore; 63using namespace HTMLNames; 64 65@interface NSView (PluginSecrets) 66- (void)setContainingWindow:(NSWindow *)w; 67@end 68 69// For compatibility only. 70@interface NSObject (OldPluginAPI) 71+ (NSView *)pluginViewWithArguments:(NSDictionary *)arguments; 72@end 73 74@interface NSView (OldPluginAPI) 75- (void)pluginInitialize; 76- (void)pluginStart; 77- (void)pluginStop; 78- (void)pluginDestroy; 79@end 80 81static NSMutableSet *pluginViews = nil; 82 83@implementation WebPluginController 84 85+ (NSView *)plugInViewWithArguments:(NSDictionary *)arguments fromPluginPackage:(WebPluginPackage *)pluginPackage 86{ 87 [pluginPackage load]; 88 Class viewFactory = [pluginPackage viewFactory]; 89 90 NSView *view = nil; 91 92 if ([viewFactory respondsToSelector:@selector(plugInViewWithArguments:)]) { 93 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 94 view = [viewFactory plugInViewWithArguments:arguments]; 95 } else if ([viewFactory respondsToSelector:@selector(pluginViewWithArguments:)]) { 96 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 97 view = [viewFactory pluginViewWithArguments:arguments]; 98 } 99 100 if (view == nil) { 101 return nil; 102 } 103 104 if (pluginViews == nil) { 105 pluginViews = [[NSMutableSet alloc] init]; 106 } 107 [pluginViews addObject:view]; 108 109 return view; 110} 111 112+ (BOOL)isPlugInView:(NSView *)view 113{ 114 return [pluginViews containsObject:view]; 115} 116 117- (id)initWithDocumentView:(NSView *)view 118{ 119 [super init]; 120 _documentView = view; 121 _views = [[NSMutableArray alloc] init]; 122 _checksInProgress = (NSMutableSet *)CFMakeCollectable(CFSetCreateMutable(NULL, 0, NULL)); 123 return self; 124} 125 126- (void)setDataSource:(WebDataSource *)dataSource 127{ 128 _dataSource = dataSource; 129} 130 131- (void)dealloc 132{ 133 [_views release]; 134 [_checksInProgress release]; 135 [super dealloc]; 136} 137 138- (void)startAllPlugins 139{ 140 if (_started) 141 return; 142 143 if ([_views count] > 0) 144 LOG(Plugins, "starting WebKit plugins : %@", [_views description]); 145 146 int i, count = [_views count]; 147 for (i = 0; i < count; i++) { 148 id aView = [_views objectAtIndex:i]; 149 if ([aView respondsToSelector:@selector(webPlugInStart)]) { 150 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 151 [aView webPlugInStart]; 152 } else if ([aView respondsToSelector:@selector(pluginStart)]) { 153 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 154 [aView pluginStart]; 155 } 156 } 157 _started = YES; 158} 159 160- (void)stopAllPlugins 161{ 162 if (!_started) 163 return; 164 165 if ([_views count] > 0) { 166 LOG(Plugins, "stopping WebKit plugins: %@", [_views description]); 167 } 168 169 int i, count = [_views count]; 170 for (i = 0; i < count; i++) { 171 id aView = [_views objectAtIndex:i]; 172 if ([aView respondsToSelector:@selector(webPlugInStop)]) { 173 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 174 [aView webPlugInStop]; 175 } else if ([aView respondsToSelector:@selector(pluginStop)]) { 176 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 177 [aView pluginStop]; 178 } 179 } 180 _started = NO; 181} 182 183- (void)addPlugin:(NSView *)view 184{ 185 if (!_documentView) { 186 LOG_ERROR("can't add a plug-in to a defunct WebPluginController"); 187 return; 188 } 189 190 if (![_views containsObject:view]) { 191 [_views addObject:view]; 192 [[_documentView _webView] addPluginInstanceView:view]; 193 194 BOOL oldDefersCallbacks = [[self webView] defersCallbacks]; 195 if (!oldDefersCallbacks) 196 [[self webView] setDefersCallbacks:YES]; 197 198 LOG(Plugins, "initializing plug-in %@", view); 199 if ([view respondsToSelector:@selector(webPlugInInitialize)]) { 200 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 201 [view webPlugInInitialize]; 202 } else if ([view respondsToSelector:@selector(pluginInitialize)]) { 203 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 204 [view pluginInitialize]; 205 } 206 207 if (!oldDefersCallbacks) 208 [[self webView] setDefersCallbacks:NO]; 209 210 if (_started) { 211 LOG(Plugins, "starting plug-in %@", view); 212 if ([view respondsToSelector:@selector(webPlugInStart)]) { 213 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 214 [view webPlugInStart]; 215 } else if ([view respondsToSelector:@selector(pluginStart)]) { 216 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 217 [view pluginStart]; 218 } 219 220 if ([view respondsToSelector:@selector(setContainingWindow:)]) { 221 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 222 [view setContainingWindow:[_documentView window]]; 223 } 224 } 225 } 226} 227 228- (void)destroyPlugin:(NSView *)view 229{ 230 if ([_views containsObject:view]) { 231 if (_started) { 232 if ([view respondsToSelector:@selector(webPlugInStop)]) { 233 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 234 [view webPlugInStop]; 235 } else if ([view respondsToSelector:@selector(pluginStop)]) { 236 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 237 [view pluginStop]; 238 } 239 } 240 241 if ([view respondsToSelector:@selector(webPlugInDestroy)]) { 242 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 243 [view webPlugInDestroy]; 244 } else if ([view respondsToSelector:@selector(pluginDestroy)]) { 245 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 246 [view pluginDestroy]; 247 } 248 249#if ENABLE(NETSCAPE_PLUGIN_API) 250 if (Frame* frame = core([self webFrame])) 251 frame->script()->cleanupScriptObjectsForPlugin(self); 252#endif 253 254 [pluginViews removeObject:view]; 255 [[_documentView _webView] removePluginInstanceView:view]; 256 [_views removeObject:view]; 257 } 258} 259 260- (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)checkIdentifier 261{ 262 [checkIdentifier cancel]; 263 [_checksInProgress removeObject:checkIdentifier]; 264} 265 266static void cancelOutstandingCheck(const void *item, void *context) 267{ 268 [(id)item cancel]; 269} 270 271- (void)_cancelOutstandingChecks 272{ 273 if (_checksInProgress) { 274 CFSetApplyFunction((CFSetRef)_checksInProgress, cancelOutstandingCheck, NULL); 275 [_checksInProgress release]; 276 _checksInProgress = nil; 277 } 278} 279 280- (void)destroyAllPlugins 281{ 282 [self stopAllPlugins]; 283 284 if ([_views count] > 0) { 285 LOG(Plugins, "destroying WebKit plugins: %@", [_views description]); 286 } 287 288 [self _cancelOutstandingChecks]; 289 290 int i, count = [_views count]; 291 for (i = 0; i < count; i++) { 292 id aView = [_views objectAtIndex:i]; 293 if ([aView respondsToSelector:@selector(webPlugInDestroy)]) { 294 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 295 [aView webPlugInDestroy]; 296 } else if ([aView respondsToSelector:@selector(pluginDestroy)]) { 297 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 298 [aView pluginDestroy]; 299 } 300 301#if ENABLE(NETSCAPE_PLUGIN_API) 302 if (Frame* frame = core([self webFrame])) 303 frame->script()->cleanupScriptObjectsForPlugin(self); 304#endif 305 306 [pluginViews removeObject:aView]; 307 [[_documentView _webView] removePluginInstanceView:aView]; 308 } 309 [_views makeObjectsPerformSelector:@selector(removeFromSuperviewWithoutNeedingDisplay)]; 310 [_views release]; 311 _views = nil; 312 313 _documentView = nil; 314} 315 316- (id)_webPluginContainerCheckIfAllowedToLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target resultObject:(id)obj selector:(SEL)selector 317{ 318 WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:request target:target resultObject:obj selector:selector controller:self contextInfo:nil]; 319 [_checksInProgress addObject:check]; 320 [check start]; 321 322 return check; 323} 324 325- (void)webPlugInContainerLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target 326{ 327 if (!request) { 328 LOG_ERROR("nil URL passed"); 329 return; 330 } 331 if (!_documentView) { 332 LOG_ERROR("could not load URL %@ because plug-in has already been destroyed", request); 333 return; 334 } 335 WebFrame *frame = [_dataSource webFrame]; 336 if (!frame) { 337 LOG_ERROR("could not load URL %@ because plug-in has already been stopped", request); 338 return; 339 } 340 if (!target) { 341 target = @"_top"; 342 } 343 NSString *JSString = [[request URL] _webkit_scriptIfJavaScriptURL]; 344 if (JSString) { 345 if ([frame findFrameNamed:target] != frame) { 346 LOG_ERROR("JavaScript requests can only be made on the frame that contains the plug-in"); 347 return; 348 } 349 [frame _stringByEvaluatingJavaScriptFromString:JSString]; 350 } else { 351 if (!request) { 352 LOG_ERROR("could not load URL %@", [request URL]); 353 return; 354 } 355 core(frame)->loader()->load(request, target, false); 356 } 357} 358 359// For compatibility only. 360- (void)showURL:(NSURL *)URL inFrame:(NSString *)target 361{ 362 [self webPlugInContainerLoadRequest:[NSURLRequest requestWithURL:URL] inFrame:target]; 363} 364 365- (void)webPlugInContainerShowStatus:(NSString *)message 366{ 367 if (!message) { 368 message = @""; 369 } 370 if (!_documentView) { 371 LOG_ERROR("could not show status message (%@) because plug-in has already been destroyed", message); 372 return; 373 } 374 WebView *v = [_dataSource _webView]; 375 [[v _UIDelegateForwarder] webView:v setStatusText:message]; 376} 377 378// For compatibility only. 379- (void)showStatus:(NSString *)message 380{ 381 [self webPlugInContainerShowStatus:message]; 382} 383 384- (NSColor *)webPlugInContainerSelectionColor 385{ 386 bool primary = true; 387 if (Frame* frame = core([self webFrame])) 388 primary = frame->selection()->isFocusedAndActive(); 389 return primary ? [NSColor selectedTextBackgroundColor] : [NSColor secondarySelectedControlColor]; 390} 391 392// For compatibility only. 393- (NSColor *)selectionColor 394{ 395 return [self webPlugInContainerSelectionColor]; 396} 397 398- (WebFrame *)webFrame 399{ 400 return [_dataSource webFrame]; 401} 402 403- (WebView *)webView 404{ 405 return [[self webFrame] webView]; 406} 407 408- (NSString *)URLPolicyCheckReferrer 409{ 410 NSURL *responseURL = [[[[self webFrame] _dataSource] response] URL]; 411 ASSERT(responseURL); 412 return [responseURL _web_originalDataAsString]; 413} 414 415- (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response 416{ 417 if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveResponse:)]) 418 [pluginView webPlugInMainResourceDidReceiveResponse:response]; 419 else { 420 // Cancel the load since this plug-in does its own loading. 421 // FIXME: See <rdar://problem/4258008> for a problem with this. 422 NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad 423 contentURL:[response URL] 424 pluginPageURL:nil 425 pluginName:nil // FIXME: Get this from somewhere 426 MIMEType:[response MIMEType]]; 427 [_dataSource _documentLoader]->cancelMainResourceLoad(error); 428 [error release]; 429 } 430} 431 432- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data 433{ 434 if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveData:)]) 435 [pluginView webPlugInMainResourceDidReceiveData:data]; 436} 437 438- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error 439{ 440 if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFailWithError:)]) 441 [pluginView webPlugInMainResourceDidFailWithError:error]; 442} 443 444- (void)pluginViewFinishedLoading:(NSView *)pluginView 445{ 446 if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFinishLoading)]) 447 [pluginView webPlugInMainResourceDidFinishLoading]; 448} 449 450#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 451static WebCore::HTMLMediaElement* mediaProxyClient(DOMElement* element) 452{ 453 if (!element) { 454 LOG_ERROR("nil element passed"); 455 return nil; 456 } 457 458 Element* node = core(element); 459 if (!node || (!node->hasTagName(HTMLNames::videoTag) && !node->hasTagName(HTMLNames::audioTag))) { 460 LOG_ERROR("invalid media element passed"); 461 return nil; 462 } 463 464 return static_cast<WebCore::HTMLMediaElement*>(node); 465} 466 467- (void)_webPluginContainerSetMediaPlayerProxy:(WebMediaPlayerProxy *)proxy forElement:(DOMElement *)element 468{ 469 WebCore::HTMLMediaElement* client = mediaProxyClient(element); 470 if (client) 471 client->setMediaPlayerProxy(proxy); 472} 473 474- (void)_webPluginContainerPostMediaPlayerNotification:(int)notification forElement:(DOMElement *)element 475{ 476 WebCore::HTMLMediaElement* client = mediaProxyClient(element); 477 if (client) 478 client->deliverNotification((MediaPlayerProxyNotificationType)notification); 479} 480#endif 481 482@end 483