diff options
Diffstat (limited to 'Source/WebKit/mac/Plugins/WebPluginDatabase.mm')
| -rw-r--r-- | Source/WebKit/mac/Plugins/WebPluginDatabase.mm | 498 |
1 files changed, 498 insertions, 0 deletions
diff --git a/Source/WebKit/mac/Plugins/WebPluginDatabase.mm b/Source/WebKit/mac/Plugins/WebPluginDatabase.mm new file mode 100644 index 0000000..e7fae1b --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginDatabase.mm @@ -0,0 +1,498 @@ +/* + * 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 "WebPluginDatabase.h" + +#import "WebBaseNetscapePluginView.h" +#import "WebBasePluginPackage.h" +#import "WebDataSourcePrivate.h" +#import "WebFrame.h" +#import "WebFrameViewInternal.h" +#import "WebHTMLRepresentation.h" +#import "WebHTMLView.h" +#import "WebKitLogging.h" +#import "WebNSFileManagerExtras.h" +#import "WebNetscapePluginPackage.h" +#import "WebPluginController.h" +#import "WebPluginPackage.h" +#import "WebViewPrivate.h" +#import "WebViewInternal.h" +#import <WebKitSystemInterface.h> +#import <wtf/Assertions.h> + +using namespace WebCore; + +static void checkCandidate(WebBasePluginPackage **currentPlugin, WebBasePluginPackage **candidatePlugin); + +@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]; +} + +static void checkCandidate(WebBasePluginPackage **currentPlugin, WebBasePluginPackage **candidatePlugin) +{ + if (!*currentPlugin) { + *currentPlugin = *candidatePlugin; + return; + } + + if ([*currentPlugin bundleIdentifier] == [*candidatePlugin bundleIdentifier] && [*candidatePlugin versionNumber] > [*currentPlugin versionNumber]) + *currentPlugin = *candidatePlugin; +} + +struct PluginPackageCandidates { + PluginPackageCandidates() + : webPlugin(nil) + , machoPlugin(nil) +#ifdef SUPPORT_CFM + , CFMPlugin(nil) +#endif + { + } + + void update(WebBasePluginPackage *plugin) + { + if ([plugin isKindOfClass:[WebPluginPackage class]]) { + checkCandidate(&webPlugin, &plugin); + return; + } + +#if ENABLE(NETSCAPE_PLUGIN_API) + if([plugin isKindOfClass:[WebNetscapePluginPackage class]]) { + WebExecutableType executableType = [(WebNetscapePluginPackage *)plugin executableType]; +#ifdef SUPPORT_CFM + if (executableType == WebCFMExecutableType) { + checkCandidate(&CFMPlugin, &plugin); + return; + } +#endif // SUPPORT_CFM + if (executableType == WebMachOExecutableType) { + checkCandidate(&machoPlugin, &plugin); + return; + } + } +#endif + ASSERT_NOT_REACHED(); + } + + WebBasePluginPackage *bestCandidate() + { + // 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; + + if (machoPlugin && ![machoPlugin isQuickTimePlugIn]) + return machoPlugin; + +#ifdef SUPPORT_CFM + if (CFMPlugin && ![CFMPlugin isQuickTimePlugIn]) + return CFMPlugin; +#endif // SUPPORT_CFM + + if (webPlugin) + return webPlugin; + if (machoPlugin) + return machoPlugin; +#ifdef SUPPORT_CFM + if (CFMPlugin) + return CFMPlugin; +#endif + return nil; + } + + WebBasePluginPackage *webPlugin; + WebBasePluginPackage *machoPlugin; +#ifdef SUPPORT_CFM + WebBasePluginPackage *CFMPlugin; +#endif +}; + +- (WebBasePluginPackage *)pluginForMIMEType:(NSString *)MIMEType +{ + PluginPackageCandidates candidates; + + MIMEType = [MIMEType lowercaseString]; + NSEnumerator *pluginEnumerator = [plugins objectEnumerator]; + + while (WebBasePluginPackage *plugin = [pluginEnumerator nextObject]) { + if ([plugin supportsMIMEType:MIMEType]) + candidates.update(plugin); + } + + return candidates.bestCandidate(); +} + +- (WebBasePluginPackage *)pluginForExtension:(NSString *)extension +{ + PluginPackageCandidates candidates; + + extension = [extension lowercaseString]; + NSEnumerator *pluginEnumerator = [plugins objectEnumerator]; + + while (WebBasePluginPackage *plugin = [pluginEnumerator nextObject]) { + if ([plugin supportsExtension:extension]) + candidates.update(plugin); + } + + WebBasePluginPackage *plugin = candidates.bestCandidate(); + + 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]; + pluginInstanceViews = [[NSMutableSet alloc] init]; + + return self; +} + +- (void)dealloc +{ + [plugInPaths release]; + [plugins release]; + [registeredMIMETypes release]; + [pluginInstanceViews 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])) { + const PluginInfo& pluginInfo = [plugin pluginInfo]; + for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) + [MIMETypes addObject:pluginInfo.mimes[i].type]; + } + + // 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 _registerPluginMIMEType:MIMEType]; + } + [MIMETypes release]; + + [pool drain]; +} + +- (BOOL)isMIMETypeRegistered:(NSString *)MIMEType +{ + return [registeredMIMETypes containsObject:MIMEType]; +} + +- (void)addPluginInstanceView:(NSView *)view +{ + [pluginInstanceViews addObject:view]; +} + +- (void)removePluginInstanceView:(NSView *)view +{ + [pluginInstanceViews removeObject:view]; +} + +- (void)removePluginInstanceViewsFor:(WebFrame*)webFrame +{ + // This handles handles the case where a frame or view is being destroyed and the plugin needs to be removed from the list first + + if( [pluginInstanceViews count] == 0 ) + return; + + NSView <WebDocumentView> *documentView = [[webFrame frameView] documentView]; + if ([documentView isKindOfClass:[WebHTMLView class]]) { + NSArray *subviews = [documentView subviews]; + unsigned int subviewCount = [subviews count]; + unsigned int subviewIndex; + + for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) { + NSView *subview = [subviews objectAtIndex:subviewIndex]; +#if ENABLE(NETSCAPE_PLUGIN_API) + if ([subview isKindOfClass:[WebBaseNetscapePluginView class]] || [WebPluginController isPlugInView:subview]) +#else + if ([WebPluginController isPlugInView:subview]) +#endif + [pluginInstanceViews removeObject:subview]; + } + } +} + +- (void)destroyAllPluginInstanceViews +{ + NSView *view; + NSArray *pli = [pluginInstanceViews allObjects]; + NSEnumerator *enumerator = [pli objectEnumerator]; + while ((view = [enumerator nextObject]) != nil) { +#if ENABLE(NETSCAPE_PLUGIN_API) + if ([view isKindOfClass:[WebBaseNetscapePluginView class]]) { + ASSERT([view respondsToSelector:@selector(stop)]); + [view performSelector:@selector(stop)]; + } else +#endif + if ([WebPluginController isPlugInView:view]) { + ASSERT([[view superview] isKindOfClass:[WebHTMLView class]]); + ASSERT([[view superview] respondsToSelector:@selector(_destroyAllWebPlugins)]); + // this will actually destroy all plugin instances for a webHTMLView and remove them from this list + [[view superview] performSelector:@selector(_destroyAllWebPlugins)]; + } + } +} + +@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 + const PluginInfo& pluginInfo = [plugin pluginInfo]; + for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) { + NSString *MIMEType = pluginInfo.mimes[i].type; + + if ([registeredMIMETypes containsObject:MIMEType]) { + if (self == sharedDatabase) + [WebView _unregisterPluginMIMEType: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 contentsOfDirectoryAtPath:pluginDirectory error:NULL] 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 |
