diff -r 000000000000 -r dd21522fd290 webengine/osswebengine/WebKit/Plugins/WebPluginDatabase.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/webengine/osswebengine/WebKit/Plugins/WebPluginDatabase.m Mon Mar 30 12:54:55 2009 +0300 @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +@interface WebPluginDatabase (Internal) ++ (NSArray *)_defaultPlugInPaths; +- (NSArray *)_plugInPaths; +- (void)_addPlugin:(WebBasePluginPackage *)plugin; +- (void)_removePlugin:(WebBasePluginPackage *)plugin; +- (NSMutableSet *)_scanForNewPlugins; +@end + +@implementation WebPluginDatabase + +static WebPluginDatabase *sharedDatabase = nil; + ++ (WebPluginDatabase *)sharedDatabase +{ + if (!sharedDatabase) { + sharedDatabase = [[WebPluginDatabase alloc] init]; + [sharedDatabase setPlugInPaths:[self _defaultPlugInPaths]]; + [sharedDatabase refresh]; + } + + return sharedDatabase; +} + ++ (void)closeSharedDatabase +{ + [sharedDatabase close]; +} + +- (WebBasePluginPackage *)pluginForKey:(NSString *)key withEnumeratorSelector:(SEL)enumeratorSelector +{ + WebBasePluginPackage *plugin = nil; + WebBasePluginPackage *webPlugin = nil; +#ifndef __LP64__ + WebBasePluginPackage *CFMPlugin = nil; + WebBasePluginPackage *machoPlugin = nil; +#endif + NSEnumerator *pluginEnumerator = [plugins objectEnumerator]; + key = [key lowercaseString]; + + while ((plugin = [pluginEnumerator nextObject]) != nil) { + if ([[[plugin performSelector:enumeratorSelector] allObjects] containsObject:key]) { + if ([plugin isKindOfClass:[WebPluginPackage class]]) { + if (!webPlugin) + webPlugin = plugin; + } +#ifndef __LP64__ + else if([plugin isKindOfClass:[WebNetscapePluginPackage class]]) { + WebExecutableType executableType = [(WebNetscapePluginPackage *)plugin executableType]; + if (executableType == WebCFMExecutableType) { + if (!CFMPlugin) + CFMPlugin = plugin; + } else if (executableType == WebMachOExecutableType) { + if (!machoPlugin) + machoPlugin = plugin; + } else { + ASSERT_NOT_REACHED(); + } + } else { + ASSERT_NOT_REACHED(); + } +#endif + } + } + + // Allow other plug-ins to win over QT because if the user has installed a plug-in that can handle a type + // that the QT plug-in can handle, they probably intended to override QT. + if (webPlugin && ![webPlugin isQuickTimePlugIn]) + return webPlugin; + +#ifndef __LP64__ + else if (machoPlugin && ![machoPlugin isQuickTimePlugIn]) + return machoPlugin; + else if (CFMPlugin && ![CFMPlugin isQuickTimePlugIn]) + return CFMPlugin; +#endif + else if (webPlugin) + return webPlugin; +#ifndef __LP64__ + else if (machoPlugin) + return machoPlugin; + else if (CFMPlugin) + return CFMPlugin; +#endif + return nil; +} + +- (WebBasePluginPackage *)pluginForMIMEType:(NSString *)MIMEType +{ + return [self pluginForKey:[MIMEType lowercaseString] + withEnumeratorSelector:@selector(MIMETypeEnumerator)]; +} + +- (WebBasePluginPackage *)pluginForExtension:(NSString *)extension +{ + WebBasePluginPackage *plugin = [self pluginForKey:[extension lowercaseString] + withEnumeratorSelector:@selector(extensionEnumerator)]; + if (!plugin) { + // If no plug-in was found from the extension, attempt to map from the extension to a MIME type + // and find the a plug-in from the MIME type. This is done in case the plug-in has not fully specified + // an extension <-> MIME type mapping. + NSString *MIMEType = WKGetMIMETypeForExtension(extension); + if ([MIMEType length] > 0) + plugin = [self pluginForMIMEType:MIMEType]; + } + return plugin; +} + +- (NSArray *)plugins +{ + return [plugins allValues]; +} + +static NSArray *additionalWebPlugInPaths; + ++ (void)setAdditionalWebPlugInPaths:(NSArray *)additionalPaths +{ + if (additionalPaths == additionalWebPlugInPaths) + return; + + [additionalWebPlugInPaths release]; + additionalWebPlugInPaths = [additionalPaths copy]; + + // One might be tempted to add additionalWebPlugInPaths to the global WebPluginDatabase here. + // For backward compatibility with earlier versions of the +setAdditionalWebPlugInPaths: SPI, + // we need to save a copy of the additional paths and not cause a refresh of the plugin DB + // at this time. + // See Radars 4608487 and 4609047. +} + +- (void)setPlugInPaths:(NSArray *)newPaths +{ + if (plugInPaths == newPaths) + return; + + [plugInPaths release]; + plugInPaths = [newPaths copy]; +} + +- (void)close +{ + NSEnumerator *pluginEnumerator = [[self plugins] objectEnumerator]; + WebBasePluginPackage *plugin; + while ((plugin = [pluginEnumerator nextObject]) != nil) + [self _removePlugin:plugin]; + [plugins release]; + plugins = nil; +} + +- (id)init +{ + if (!(self = [super init])) + return nil; + + registeredMIMETypes = [[NSMutableSet alloc] init]; + + return self; +} + +- (void)dealloc +{ + [plugInPaths release]; + [plugins release]; + [registeredMIMETypes release]; + + [super dealloc]; +} + +- (void)refresh +{ + // This method does a bit of autoreleasing, so create an autorelease pool to ensure that calling + // -refresh multiple times does not bloat the default pool. + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + // Create map from plug-in path to WebBasePluginPackage + if (!plugins) + plugins = [[NSMutableDictionary alloc] initWithCapacity:12]; + + // Find all plug-ins on disk + NSMutableSet *newPlugins = [self _scanForNewPlugins]; + + // Find plug-ins to remove from database (i.e., plug-ins that no longer exist on disk) + NSMutableSet *pluginsToRemove = [NSMutableSet set]; + NSEnumerator *pluginEnumerator = [plugins objectEnumerator]; + WebBasePluginPackage *plugin; + while ((plugin = [pluginEnumerator nextObject]) != nil) { + // Any plug-ins that were removed from disk since the last refresh should be removed from + // the database. + if (![newPlugins containsObject:plugin]) + [pluginsToRemove addObject:plugin]; + + // Remove every member of 'plugins' from 'newPlugins'. After this loop exits, 'newPlugins' + // will be the set of new plug-ins that should be added to the database. + [newPlugins removeObject:plugin]; + } + +#if !LOG_DISABLED + if ([newPlugins count] > 0) + LOG(Plugins, "New plugins:\n%@", newPlugins); + if ([pluginsToRemove count] > 0) + LOG(Plugins, "Removed plugins:\n%@", pluginsToRemove); +#endif + + // Remove plugins from database + pluginEnumerator = [pluginsToRemove objectEnumerator]; + while ((plugin = [pluginEnumerator nextObject]) != nil) + [self _removePlugin:plugin]; + + // Add new plugins to database + pluginEnumerator = [newPlugins objectEnumerator]; + while ((plugin = [pluginEnumerator nextObject]) != nil) + [self _addPlugin:plugin]; + + // Build a list of MIME types. + NSMutableSet *MIMETypes = [[NSMutableSet alloc] init]; + pluginEnumerator = [plugins objectEnumerator]; + while ((plugin = [pluginEnumerator nextObject]) != nil) + [MIMETypes addObjectsFromArray:[[plugin MIMETypeEnumerator] allObjects]]; + + // Register plug-in views and representations. + NSEnumerator *MIMEEnumerator = [MIMETypes objectEnumerator]; + NSString *MIMEType; + while ((MIMEType = [MIMEEnumerator nextObject]) != nil) { + [registeredMIMETypes addObject:MIMEType]; + + if ([WebView canShowMIMETypeAsHTML:MIMEType]) + // Don't allow plug-ins to override our core HTML types. + continue; + plugin = [self pluginForMIMEType:MIMEType]; + if ([plugin isJavaPlugIn]) + // Don't register the Java plug-in for a document view since Java files should be downloaded when not embedded. + continue; + if ([plugin isQuickTimePlugIn] && [[WebFrameView _viewTypesAllowImageTypeOmission:NO] objectForKey:MIMEType]) + // Don't allow the QT plug-in to override any types because it claims many that we can handle ourselves. + continue; + + if (self == sharedDatabase) + [WebView registerViewClass:[WebHTMLView class] representationClass:[WebHTMLRepresentation class] forMIMEType:MIMEType]; + } + [MIMETypes release]; + + [pool drain]; +} + +- (BOOL)isMIMETypeRegistered:(NSString *)MIMEType +{ + return [registeredMIMETypes containsObject:MIMEType]; +} + +@end + +@implementation WebPluginDatabase (Internal) + ++ (NSArray *)_defaultPlugInPaths +{ + // Plug-ins are found in order of precedence. + // If there are duplicates, the first found plug-in is used. + // For example, if there is a QuickTime.plugin in the users's home directory + // that is used instead of the /Library/Internet Plug-ins version. + // The purpose is to allow non-admin users to update their plug-ins. + return [NSArray arrayWithObjects: + [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Internet Plug-Ins"], + @"/Library/Internet Plug-Ins", + [[NSBundle mainBundle] builtInPlugInsPath], + nil]; +} + +- (NSArray *)_plugInPaths +{ + if (self == sharedDatabase && additionalWebPlugInPaths) { + // Add additionalWebPlugInPaths to the global WebPluginDatabase. We do this here for + // backward compatibility with earlier versions of the +setAdditionalWebPlugInPaths: SPI, + // which simply saved a copy of the additional paths and did not cause the plugin DB to + // refresh. See Radars 4608487 and 4609047. + NSMutableArray *modifiedPlugInPaths = [[plugInPaths mutableCopy] autorelease]; + [modifiedPlugInPaths addObjectsFromArray:additionalWebPlugInPaths]; + return modifiedPlugInPaths; + } else + return plugInPaths; +} + +- (void)_addPlugin:(WebBasePluginPackage *)plugin +{ + ASSERT(plugin); + NSString *pluginPath = [plugin path]; + ASSERT(pluginPath); + [plugins setObject:plugin forKey:pluginPath]; + [plugin wasAddedToPluginDatabase:self]; +} + +- (void)_removePlugin:(WebBasePluginPackage *)plugin +{ + ASSERT(plugin); + + // Unregister plug-in's MIME type registrations + NSEnumerator *MIMETypeEnumerator = [plugin MIMETypeEnumerator]; + NSString *MIMEType; + while ((MIMEType = [MIMETypeEnumerator nextObject])) { + if ([registeredMIMETypes containsObject:MIMEType]) { + if (self == sharedDatabase) + [WebView _unregisterViewClassAndRepresentationClassForMIMEType:MIMEType]; + [registeredMIMETypes removeObject:MIMEType]; + } + } + + // Remove plug-in from database + NSString *pluginPath = [plugin path]; + ASSERT(pluginPath); + [plugin retain]; + [plugins removeObjectForKey:pluginPath]; + [plugin wasRemovedFromPluginDatabase:self]; + [plugin release]; +} + +- (NSMutableSet *)_scanForNewPlugins +{ + NSMutableSet *newPlugins = [NSMutableSet set]; + NSEnumerator *directoryEnumerator = [[self _plugInPaths] objectEnumerator]; + NSMutableSet *uniqueFilenames = [[NSMutableSet alloc] init]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString *pluginDirectory; + while ((pluginDirectory = [directoryEnumerator nextObject]) != nil) { + // Get contents of each plug-in directory + NSEnumerator *filenameEnumerator = [[fileManager directoryContentsAtPath:pluginDirectory] objectEnumerator]; + NSString *filename; + while ((filename = [filenameEnumerator nextObject]) != nil) { + // Unique plug-ins by filename + if ([uniqueFilenames containsObject:filename]) + continue; + [uniqueFilenames addObject:filename]; + + // Create a plug-in package for this path + NSString *pluginPath = [pluginDirectory stringByAppendingPathComponent:filename]; + WebBasePluginPackage *pluginPackage = [plugins objectForKey:pluginPath]; + if (!pluginPackage) + pluginPackage = [WebBasePluginPackage pluginWithPath:pluginPath]; + if (pluginPackage) + [newPlugins addObject:pluginPackage]; + } + } + [uniqueFilenames release]; + + return newPlugins; +} + +@end