Home | History | Annotate | Download | only in Plugins
      1 /*
      2  * Copyright (C) 2005 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 #import "WebPluginDatabase.h"
     30 
     31 #import "WebBaseNetscapePluginView.h"
     32 #import "WebBasePluginPackage.h"
     33 #import "WebDataSourcePrivate.h"
     34 #import "WebFrame.h"
     35 #import "WebFrameViewInternal.h"
     36 #import "WebHTMLRepresentation.h"
     37 #import "WebHTMLView.h"
     38 #import "WebHTMLView.h"
     39 #import "WebKitLogging.h"
     40 #import "WebNSFileManagerExtras.h"
     41 #import "WebNetscapePluginPackage.h"
     42 #import "WebPluginController.h"
     43 #import "WebPluginPackage.h"
     44 #import "WebViewPrivate.h"
     45 #import <WebKitSystemInterface.h>
     46 #import <wtf/Assertions.h>
     47 
     48 static void checkCandidate(WebBasePluginPackage **currentPlugin, WebBasePluginPackage **candidatePlugin);
     49 
     50 @interface WebPluginDatabase (Internal)
     51 + (NSArray *)_defaultPlugInPaths;
     52 - (NSArray *)_plugInPaths;
     53 - (void)_addPlugin:(WebBasePluginPackage *)plugin;
     54 - (void)_removePlugin:(WebBasePluginPackage *)plugin;
     55 - (NSMutableSet *)_scanForNewPlugins;
     56 @end
     57 
     58 @implementation WebPluginDatabase
     59 
     60 static WebPluginDatabase *sharedDatabase = nil;
     61 
     62 + (WebPluginDatabase *)sharedDatabase
     63 {
     64     if (!sharedDatabase) {
     65         sharedDatabase = [[WebPluginDatabase alloc] init];
     66         [sharedDatabase setPlugInPaths:[self _defaultPlugInPaths]];
     67         [sharedDatabase refresh];
     68     }
     69 
     70     return sharedDatabase;
     71 }
     72 
     73 + (void)closeSharedDatabase
     74 {
     75     [sharedDatabase close];
     76 }
     77 
     78 static void checkCandidate(WebBasePluginPackage **currentPlugin, WebBasePluginPackage **candidatePlugin)
     79 {
     80     if (!*currentPlugin) {
     81         *currentPlugin = *candidatePlugin;
     82         return;
     83     }
     84 
     85     if ([[[*currentPlugin bundle] bundleIdentifier] isEqualToString:[[*candidatePlugin bundle] bundleIdentifier]] && [*candidatePlugin versionNumber] > [*currentPlugin versionNumber])
     86         *currentPlugin = *candidatePlugin;
     87 }
     88 
     89 - (WebBasePluginPackage *)pluginForKey:(NSString *)key withEnumeratorSelector:(SEL)enumeratorSelector
     90 {
     91     WebBasePluginPackage *plugin = nil;
     92     WebBasePluginPackage *webPlugin = nil;
     93 #ifdef SUPPORT_CFM
     94     WebBasePluginPackage *CFMPlugin = nil;
     95 #endif
     96     WebBasePluginPackage *machoPlugin = nil;
     97 
     98     NSEnumerator *pluginEnumerator = [plugins objectEnumerator];
     99     key = [key lowercaseString];
    100 
    101     while ((plugin = [pluginEnumerator nextObject]) != nil) {
    102         if ([[[plugin performSelector:enumeratorSelector] allObjects] containsObject:key]) {
    103             if ([plugin isKindOfClass:[WebPluginPackage class]])
    104                 checkCandidate(&webPlugin, &plugin);
    105 #if ENABLE(NETSCAPE_PLUGIN_API)
    106             else if([plugin isKindOfClass:[WebNetscapePluginPackage class]]) {
    107                 WebExecutableType executableType = [(WebNetscapePluginPackage *)plugin executableType];
    108 #ifdef SUPPORT_CFM
    109                 if (executableType == WebCFMExecutableType) {
    110                     checkCandidate(&CFMPlugin, &plugin);
    111                 } else
    112 #endif // SUPPORT_CFM
    113                 if (executableType == WebMachOExecutableType) {
    114                     checkCandidate(&machoPlugin, &plugin);
    115                 } else {
    116                     ASSERT_NOT_REACHED();
    117                 }
    118             } else {
    119                 ASSERT_NOT_REACHED();
    120             }
    121 #endif
    122         }
    123     }
    124 
    125     // Allow other plug-ins to win over QT because if the user has installed a plug-in that can handle a type
    126     // that the QT plug-in can handle, they probably intended to override QT.
    127     if (webPlugin && ![webPlugin isQuickTimePlugIn])
    128         return webPlugin;
    129 
    130     else if (machoPlugin && ![machoPlugin isQuickTimePlugIn])
    131         return machoPlugin;
    132 #ifdef SUPPORT_CFM
    133     else if (CFMPlugin && ![CFMPlugin isQuickTimePlugIn])
    134         return CFMPlugin;
    135 #endif // SUPPORT_CFM
    136     else if (webPlugin)
    137         return webPlugin;
    138     else if (machoPlugin)
    139         return machoPlugin;
    140 #ifdef SUPPORT_CFM
    141     else if (CFMPlugin)
    142         return CFMPlugin;
    143 #endif
    144     return nil;
    145 }
    146 
    147 - (WebBasePluginPackage *)pluginForMIMEType:(NSString *)MIMEType
    148 {
    149     return [self pluginForKey:[MIMEType lowercaseString]
    150        withEnumeratorSelector:@selector(MIMETypeEnumerator)];
    151 }
    152 
    153 - (WebBasePluginPackage *)pluginForExtension:(NSString *)extension
    154 {
    155     WebBasePluginPackage *plugin = [self pluginForKey:[extension lowercaseString]
    156                                withEnumeratorSelector:@selector(extensionEnumerator)];
    157     if (!plugin) {
    158         // If no plug-in was found from the extension, attempt to map from the extension to a MIME type
    159         // and find the a plug-in from the MIME type. This is done in case the plug-in has not fully specified
    160         // an extension <-> MIME type mapping.
    161         NSString *MIMEType = WKGetMIMETypeForExtension(extension);
    162         if ([MIMEType length] > 0)
    163             plugin = [self pluginForMIMEType:MIMEType];
    164     }
    165     return plugin;
    166 }
    167 
    168 - (NSArray *)plugins
    169 {
    170     return [plugins allValues];
    171 }
    172 
    173 static NSArray *additionalWebPlugInPaths;
    174 
    175 + (void)setAdditionalWebPlugInPaths:(NSArray *)additionalPaths
    176 {
    177     if (additionalPaths == additionalWebPlugInPaths)
    178         return;
    179 
    180     [additionalWebPlugInPaths release];
    181     additionalWebPlugInPaths = [additionalPaths copy];
    182 
    183     // One might be tempted to add additionalWebPlugInPaths to the global WebPluginDatabase here.
    184     // For backward compatibility with earlier versions of the +setAdditionalWebPlugInPaths: SPI,
    185     // we need to save a copy of the additional paths and not cause a refresh of the plugin DB
    186     // at this time.
    187     // See Radars 4608487 and 4609047.
    188 }
    189 
    190 - (void)setPlugInPaths:(NSArray *)newPaths
    191 {
    192     if (plugInPaths == newPaths)
    193         return;
    194 
    195     [plugInPaths release];
    196     plugInPaths = [newPaths copy];
    197 }
    198 
    199 - (void)close
    200 {
    201     NSEnumerator *pluginEnumerator = [[self plugins] objectEnumerator];
    202     WebBasePluginPackage *plugin;
    203     while ((plugin = [pluginEnumerator nextObject]) != nil)
    204         [self _removePlugin:plugin];
    205     [plugins release];
    206     plugins = nil;
    207 }
    208 
    209 - (id)init
    210 {
    211     if (!(self = [super init]))
    212         return nil;
    213 
    214     registeredMIMETypes = [[NSMutableSet alloc] init];
    215     pluginInstanceViews = [[NSMutableSet alloc] init];
    216 
    217     return self;
    218 }
    219 
    220 - (void)dealloc
    221 {
    222     [plugInPaths release];
    223     [plugins release];
    224     [registeredMIMETypes release];
    225     [pluginInstanceViews release];
    226 
    227     [super dealloc];
    228 }
    229 
    230 - (void)refresh
    231 {
    232     // This method does a bit of autoreleasing, so create an autorelease pool to ensure that calling
    233     // -refresh multiple times does not bloat the default pool.
    234     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    235 
    236     // Create map from plug-in path to WebBasePluginPackage
    237     if (!plugins)
    238         plugins = [[NSMutableDictionary alloc] initWithCapacity:12];
    239 
    240     // Find all plug-ins on disk
    241     NSMutableSet *newPlugins = [self _scanForNewPlugins];
    242 
    243     // Find plug-ins to remove from database (i.e., plug-ins that no longer exist on disk)
    244     NSMutableSet *pluginsToRemove = [NSMutableSet set];
    245     NSEnumerator *pluginEnumerator = [plugins objectEnumerator];
    246     WebBasePluginPackage *plugin;
    247     while ((plugin = [pluginEnumerator nextObject]) != nil) {
    248         // Any plug-ins that were removed from disk since the last refresh should be removed from
    249         // the database.
    250         if (![newPlugins containsObject:plugin])
    251             [pluginsToRemove addObject:plugin];
    252 
    253         // Remove every member of 'plugins' from 'newPlugins'.  After this loop exits, 'newPlugins'
    254         // will be the set of new plug-ins that should be added to the database.
    255         [newPlugins removeObject:plugin];
    256     }
    257 
    258 #if !LOG_DISABLED
    259     if ([newPlugins count] > 0)
    260         LOG(Plugins, "New plugins:\n%@", newPlugins);
    261     if ([pluginsToRemove count] > 0)
    262         LOG(Plugins, "Removed plugins:\n%@", pluginsToRemove);
    263 #endif
    264 
    265     // Remove plugins from database
    266     pluginEnumerator = [pluginsToRemove objectEnumerator];
    267     while ((plugin = [pluginEnumerator nextObject]) != nil)
    268         [self _removePlugin:plugin];
    269 
    270     // Add new plugins to database
    271     pluginEnumerator = [newPlugins objectEnumerator];
    272     while ((plugin = [pluginEnumerator nextObject]) != nil)
    273         [self _addPlugin:plugin];
    274 
    275     // Build a list of MIME types.
    276     NSMutableSet *MIMETypes = [[NSMutableSet alloc] init];
    277     pluginEnumerator = [plugins objectEnumerator];
    278     while ((plugin = [pluginEnumerator nextObject]) != nil)
    279         [MIMETypes addObjectsFromArray:[[plugin MIMETypeEnumerator] allObjects]];
    280 
    281     // Register plug-in views and representations.
    282     NSEnumerator *MIMEEnumerator = [MIMETypes objectEnumerator];
    283     NSString *MIMEType;
    284     while ((MIMEType = [MIMEEnumerator nextObject]) != nil) {
    285         [registeredMIMETypes addObject:MIMEType];
    286 
    287         if ([WebView canShowMIMETypeAsHTML:MIMEType])
    288             // Don't allow plug-ins to override our core HTML types.
    289             continue;
    290         plugin = [self pluginForMIMEType:MIMEType];
    291         if ([plugin isJavaPlugIn])
    292             // Don't register the Java plug-in for a document view since Java files should be downloaded when not embedded.
    293             continue;
    294         if ([plugin isQuickTimePlugIn] && [[WebFrameView _viewTypesAllowImageTypeOmission:NO] objectForKey:MIMEType])
    295             // Don't allow the QT plug-in to override any types because it claims many that we can handle ourselves.
    296             continue;
    297 
    298         if (self == sharedDatabase)
    299             [WebView registerViewClass:[WebHTMLView class] representationClass:[WebHTMLRepresentation class] forMIMEType:MIMEType];
    300     }
    301     [MIMETypes release];
    302 
    303     [pool drain];
    304 }
    305 
    306 - (BOOL)isMIMETypeRegistered:(NSString *)MIMEType
    307 {
    308     return [registeredMIMETypes containsObject:MIMEType];
    309 }
    310 
    311 - (void)addPluginInstanceView:(NSView *)view
    312 {
    313     [pluginInstanceViews addObject:view];
    314 }
    315 
    316 - (void)removePluginInstanceView:(NSView *)view
    317 {
    318     [pluginInstanceViews removeObject:view];
    319 }
    320 
    321 - (void)removePluginInstanceViewsFor:(WebFrame*)webFrame
    322 {
    323     // This handles handles the case where a frame or view is being destroyed and the plugin needs to be removed from the list first
    324 
    325     if( [pluginInstanceViews count] == 0 )
    326         return;
    327 
    328     NSView <WebDocumentView> *documentView = [[webFrame frameView] documentView];
    329     if ([documentView isKindOfClass:[WebHTMLView class]]) {
    330         NSArray *subviews = [documentView subviews];
    331         unsigned int subviewCount = [subviews count];
    332         unsigned int subviewIndex;
    333 
    334         for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
    335             NSView *subview = [subviews objectAtIndex:subviewIndex];
    336 #if ENABLE(NETSCAPE_PLUGIN_API)
    337             if ([subview isKindOfClass:[WebBaseNetscapePluginView class]] || [WebPluginController isPlugInView:subview])
    338 #else
    339             if ([WebPluginController isPlugInView:subview])
    340 #endif
    341                 [pluginInstanceViews removeObject:subview];
    342         }
    343     }
    344 }
    345 
    346 - (void)destroyAllPluginInstanceViews
    347 {
    348     NSView *view;
    349     NSArray *pli = [pluginInstanceViews allObjects];
    350     NSEnumerator *enumerator = [pli objectEnumerator];
    351     while ((view = [enumerator nextObject]) != nil) {
    352 #if ENABLE(NETSCAPE_PLUGIN_API)
    353         if ([view isKindOfClass:[WebBaseNetscapePluginView class]]) {
    354             ASSERT([view respondsToSelector:@selector(stop)]);
    355             [view performSelector:@selector(stop)];
    356         } else
    357 #endif
    358         if ([WebPluginController isPlugInView:view]) {
    359             ASSERT([[view superview] isKindOfClass:[WebHTMLView class]]);
    360             ASSERT([[view superview] respondsToSelector:@selector(_destroyAllWebPlugins)]);
    361             // this will actually destroy all plugin instances for a webHTMLView and remove them from this list
    362             [[view superview] performSelector:@selector(_destroyAllWebPlugins)];
    363         }
    364     }
    365 }
    366 
    367 @end
    368 
    369 @implementation WebPluginDatabase (Internal)
    370 
    371 + (NSArray *)_defaultPlugInPaths
    372 {
    373     // Plug-ins are found in order of precedence.
    374     // If there are duplicates, the first found plug-in is used.
    375     // For example, if there is a QuickTime.plugin in the users's home directory
    376     // that is used instead of the /Library/Internet Plug-ins version.
    377     // The purpose is to allow non-admin users to update their plug-ins.
    378     return [NSArray arrayWithObjects:
    379         [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Internet Plug-Ins"],
    380         @"/Library/Internet Plug-Ins",
    381         [[NSBundle mainBundle] builtInPlugInsPath],
    382         nil];
    383 }
    384 
    385 - (NSArray *)_plugInPaths
    386 {
    387     if (self == sharedDatabase && additionalWebPlugInPaths) {
    388         // Add additionalWebPlugInPaths to the global WebPluginDatabase.  We do this here for
    389         // backward compatibility with earlier versions of the +setAdditionalWebPlugInPaths: SPI,
    390         // which simply saved a copy of the additional paths and did not cause the plugin DB to
    391         // refresh.  See Radars 4608487 and 4609047.
    392         NSMutableArray *modifiedPlugInPaths = [[plugInPaths mutableCopy] autorelease];
    393         [modifiedPlugInPaths addObjectsFromArray:additionalWebPlugInPaths];
    394         return modifiedPlugInPaths;
    395     } else
    396         return plugInPaths;
    397 }
    398 
    399 - (void)_addPlugin:(WebBasePluginPackage *)plugin
    400 {
    401     ASSERT(plugin);
    402     NSString *pluginPath = [plugin path];
    403     ASSERT(pluginPath);
    404     [plugins setObject:plugin forKey:pluginPath];
    405     [plugin wasAddedToPluginDatabase:self];
    406 }
    407 
    408 - (void)_removePlugin:(WebBasePluginPackage *)plugin
    409 {
    410     ASSERT(plugin);
    411 
    412     // Unregister plug-in's MIME type registrations
    413     NSEnumerator *MIMETypeEnumerator = [plugin MIMETypeEnumerator];
    414     NSString *MIMEType;
    415     while ((MIMEType = [MIMETypeEnumerator nextObject])) {
    416         if ([registeredMIMETypes containsObject:MIMEType]) {
    417             if (self == sharedDatabase)
    418                 [WebView _unregisterViewClassAndRepresentationClassForMIMEType:MIMEType];
    419             [registeredMIMETypes removeObject:MIMEType];
    420         }
    421     }
    422 
    423     // Remove plug-in from database
    424     NSString *pluginPath = [plugin path];
    425     ASSERT(pluginPath);
    426     [plugin retain];
    427     [plugins removeObjectForKey:pluginPath];
    428     [plugin wasRemovedFromPluginDatabase:self];
    429     [plugin release];
    430 }
    431 
    432 - (NSMutableSet *)_scanForNewPlugins
    433 {
    434     NSMutableSet *newPlugins = [NSMutableSet set];
    435     NSEnumerator *directoryEnumerator = [[self _plugInPaths] objectEnumerator];
    436     NSMutableSet *uniqueFilenames = [[NSMutableSet alloc] init];
    437     NSFileManager *fileManager = [NSFileManager defaultManager];
    438     NSString *pluginDirectory;
    439     while ((pluginDirectory = [directoryEnumerator nextObject]) != nil) {
    440         // Get contents of each plug-in directory
    441         NSEnumerator *filenameEnumerator = [[fileManager contentsOfDirectoryAtPath:pluginDirectory error:NULL] objectEnumerator];
    442         NSString *filename;
    443         while ((filename = [filenameEnumerator nextObject]) != nil) {
    444             // Unique plug-ins by filename
    445             if ([uniqueFilenames containsObject:filename])
    446                 continue;
    447             [uniqueFilenames addObject:filename];
    448 
    449             // Create a plug-in package for this path
    450             NSString *pluginPath = [pluginDirectory stringByAppendingPathComponent:filename];
    451             WebBasePluginPackage *pluginPackage = [plugins objectForKey:pluginPath];
    452             if (!pluginPackage)
    453                 pluginPackage = [WebBasePluginPackage pluginWithPath:pluginPath];
    454             if (pluginPackage)
    455                 [newPlugins addObject:pluginPackage];
    456         }
    457     }
    458     [uniqueFilenames release];
    459 
    460     return newPlugins;
    461 }
    462 
    463 @end
    464