Home | History | Annotate | Download | only in Plugins
      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