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)stopOnePlugin:(NSView *)view 139{ 140 if ([view respondsToSelector:@selector(webPlugInStop)]) { 141 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 142 [view webPlugInStop]; 143 } else if ([view respondsToSelector:@selector(pluginStop)]) { 144 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 145 [view pluginStop]; 146 } 147} 148 149- (void)destroyOnePlugin:(NSView *)view 150{ 151 if ([view respondsToSelector:@selector(webPlugInDestroy)]) { 152 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 153 [view webPlugInDestroy]; 154 } else if ([view respondsToSelector:@selector(pluginDestroy)]) { 155 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 156 [view pluginDestroy]; 157 } 158} 159 160- (void)startAllPlugins 161{ 162 if (_started) 163 return; 164 165 if ([_views count] > 0) 166 LOG(Plugins, "starting WebKit plugins : %@", [_views description]); 167 168 int i, count = [_views count]; 169 for (i = 0; i < count; i++) { 170 id aView = [_views objectAtIndex:i]; 171 if ([aView respondsToSelector:@selector(webPlugInStart)]) { 172 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 173 [aView webPlugInStart]; 174 } else if ([aView respondsToSelector:@selector(pluginStart)]) { 175 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 176 [aView pluginStart]; 177 } 178 } 179 _started = YES; 180} 181 182- (void)stopAllPlugins 183{ 184 if (!_started) 185 return; 186 187 if ([_views count] > 0) { 188 LOG(Plugins, "stopping WebKit plugins: %@", [_views description]); 189 } 190 191 int i, count = [_views count]; 192 for (i = 0; i < count; i++) 193 [self stopOnePlugin:[_views objectAtIndex:i]]; 194 195 _started = NO; 196} 197 198- (void)addPlugin:(NSView *)view 199{ 200 if (!_documentView) { 201 LOG_ERROR("can't add a plug-in to a defunct WebPluginController"); 202 return; 203 } 204 205 if (![_views containsObject:view]) { 206 [_views addObject:view]; 207 [[_documentView _webView] addPluginInstanceView:view]; 208 209 BOOL oldDefersCallbacks = [[self webView] defersCallbacks]; 210 if (!oldDefersCallbacks) 211 [[self webView] setDefersCallbacks:YES]; 212 213 LOG(Plugins, "initializing plug-in %@", view); 214 if ([view respondsToSelector:@selector(webPlugInInitialize)]) { 215 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 216 [view webPlugInInitialize]; 217 } else if ([view respondsToSelector:@selector(pluginInitialize)]) { 218 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 219 [view pluginInitialize]; 220 } 221 222 if (!oldDefersCallbacks) 223 [[self webView] setDefersCallbacks:NO]; 224 225 if (_started) { 226 LOG(Plugins, "starting plug-in %@", view); 227 if ([view respondsToSelector:@selector(webPlugInStart)]) { 228 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 229 [view webPlugInStart]; 230 } else if ([view respondsToSelector:@selector(pluginStart)]) { 231 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 232 [view pluginStart]; 233 } 234 235 if ([view respondsToSelector:@selector(setContainingWindow:)]) { 236 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 237 [view setContainingWindow:[_documentView window]]; 238 } 239 } 240 } 241} 242 243- (void)destroyPlugin:(NSView *)view 244{ 245 if ([_views containsObject:view]) { 246 if (_started) 247 [self stopOnePlugin:view]; 248 [self destroyOnePlugin:view]; 249 250#if ENABLE(NETSCAPE_PLUGIN_API) 251 if (Frame* frame = core([self webFrame])) 252 frame->script()->cleanupScriptObjectsForPlugin(self); 253#endif 254 255 [pluginViews removeObject:view]; 256 [[_documentView _webView] removePluginInstanceView:view]; 257 [_views removeObject:view]; 258 } 259} 260 261- (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)checkIdentifier 262{ 263 [checkIdentifier cancel]; 264 [_checksInProgress removeObject:checkIdentifier]; 265} 266 267static void cancelOutstandingCheck(const void *item, void *context) 268{ 269 [(id)item cancel]; 270} 271 272- (void)_cancelOutstandingChecks 273{ 274 if (_checksInProgress) { 275 CFSetApplyFunction((CFSetRef)_checksInProgress, cancelOutstandingCheck, NULL); 276 [_checksInProgress release]; 277 _checksInProgress = nil; 278 } 279} 280 281- (void)destroyAllPlugins 282{ 283 [self stopAllPlugins]; 284 285 if ([_views count] > 0) { 286 LOG(Plugins, "destroying WebKit plugins: %@", [_views description]); 287 } 288 289 [self _cancelOutstandingChecks]; 290 291 int i, count = [_views count]; 292 for (i = 0; i < count; i++) { 293 id aView = [_views objectAtIndex:i]; 294 [self destroyOnePlugin:aView]; 295 296#if ENABLE(NETSCAPE_PLUGIN_API) 297 if (Frame* frame = core([self webFrame])) 298 frame->script()->cleanupScriptObjectsForPlugin(self); 299#endif 300 301 [pluginViews removeObject:aView]; 302 [[_documentView _webView] removePluginInstanceView:aView]; 303 } 304 [_views makeObjectsPerformSelector:@selector(removeFromSuperviewWithoutNeedingDisplay)]; 305 [_views release]; 306 _views = nil; 307 308 _documentView = nil; 309} 310 311- (id)_webPluginContainerCheckIfAllowedToLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target resultObject:(id)obj selector:(SEL)selector 312{ 313 WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:request target:target resultObject:obj selector:selector controller:self contextInfo:nil]; 314 [_checksInProgress addObject:check]; 315 [check start]; 316 317 return check; 318} 319 320- (void)webPlugInContainerLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target 321{ 322 if (!request) { 323 LOG_ERROR("nil URL passed"); 324 return; 325 } 326 if (!_documentView) { 327 LOG_ERROR("could not load URL %@ because plug-in has already been destroyed", request); 328 return; 329 } 330 WebFrame *frame = [_dataSource webFrame]; 331 if (!frame) { 332 LOG_ERROR("could not load URL %@ because plug-in has already been stopped", request); 333 return; 334 } 335 if (!target) { 336 target = @"_top"; 337 } 338 NSString *JSString = [[request URL] _webkit_scriptIfJavaScriptURL]; 339 if (JSString) { 340 if ([frame findFrameNamed:target] != frame) { 341 LOG_ERROR("JavaScript requests can only be made on the frame that contains the plug-in"); 342 return; 343 } 344 [frame _stringByEvaluatingJavaScriptFromString:JSString]; 345 } else { 346 if (!request) { 347 LOG_ERROR("could not load URL %@", [request URL]); 348 return; 349 } 350 core(frame)->loader()->load(request, target, false); 351 } 352} 353 354// For compatibility only. 355- (void)showURL:(NSURL *)URL inFrame:(NSString *)target 356{ 357 [self webPlugInContainerLoadRequest:[NSURLRequest requestWithURL:URL] inFrame:target]; 358} 359 360- (void)webPlugInContainerShowStatus:(NSString *)message 361{ 362 if (!message) { 363 message = @""; 364 } 365 if (!_documentView) { 366 LOG_ERROR("could not show status message (%@) because plug-in has already been destroyed", message); 367 return; 368 } 369 WebView *v = [_dataSource _webView]; 370 [[v _UIDelegateForwarder] webView:v setStatusText:message]; 371} 372 373// For compatibility only. 374- (void)showStatus:(NSString *)message 375{ 376 [self webPlugInContainerShowStatus:message]; 377} 378 379- (NSColor *)webPlugInContainerSelectionColor 380{ 381 bool primary = true; 382 if (Frame* frame = core([self webFrame])) 383 primary = frame->selection()->isFocusedAndActive(); 384 return primary ? [NSColor selectedTextBackgroundColor] : [NSColor secondarySelectedControlColor]; 385} 386 387// For compatibility only. 388- (NSColor *)selectionColor 389{ 390 return [self webPlugInContainerSelectionColor]; 391} 392 393- (WebFrame *)webFrame 394{ 395 return [_dataSource webFrame]; 396} 397 398- (WebView *)webView 399{ 400 return [[self webFrame] webView]; 401} 402 403- (NSString *)URLPolicyCheckReferrer 404{ 405 NSURL *responseURL = [[[[self webFrame] _dataSource] response] URL]; 406 ASSERT(responseURL); 407 return [responseURL _web_originalDataAsString]; 408} 409 410- (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response 411{ 412 if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveResponse:)]) 413 [pluginView webPlugInMainResourceDidReceiveResponse:response]; 414 else { 415 // Cancel the load since this plug-in does its own loading. 416 // FIXME: See <rdar://problem/4258008> for a problem with this. 417 NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad 418 contentURL:[response URL] 419 pluginPageURL:nil 420 pluginName:nil // FIXME: Get this from somewhere 421 MIMEType:[response MIMEType]]; 422 [_dataSource _documentLoader]->cancelMainResourceLoad(error); 423 [error release]; 424 } 425} 426 427- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data 428{ 429 if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveData:)]) 430 [pluginView webPlugInMainResourceDidReceiveData:data]; 431} 432 433- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error 434{ 435 if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFailWithError:)]) 436 [pluginView webPlugInMainResourceDidFailWithError:error]; 437} 438 439- (void)pluginViewFinishedLoading:(NSView *)pluginView 440{ 441 if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFinishLoading)]) 442 [pluginView webPlugInMainResourceDidFinishLoading]; 443} 444 445#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) 446static WebCore::HTMLMediaElement* mediaProxyClient(DOMElement* element) 447{ 448 if (!element) { 449 LOG_ERROR("nil element passed"); 450 return nil; 451 } 452 453 Element* node = core(element); 454 if (!node || (!node->hasTagName(HTMLNames::videoTag) && !node->hasTagName(HTMLNames::audioTag))) { 455 LOG_ERROR("invalid media element passed"); 456 return nil; 457 } 458 459 return static_cast<WebCore::HTMLMediaElement*>(node); 460} 461 462- (void)_webPluginContainerSetMediaPlayerProxy:(WebMediaPlayerProxy *)proxy forElement:(DOMElement *)element 463{ 464 WebCore::HTMLMediaElement* client = mediaProxyClient(element); 465 if (client) 466 client->setMediaPlayerProxy(proxy); 467} 468 469- (void)_webPluginContainerPostMediaPlayerNotification:(int)notification forElement:(DOMElement *)element 470{ 471 WebCore::HTMLMediaElement* client = mediaProxyClient(element); 472 if (client) 473 client->deliverNotification((MediaPlayerProxyNotificationType)notification); 474} 475#endif 476 477@end 478