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 62 using namespace WebCore; 63 using 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 81 static 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 267 static 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) 446 static 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