2 * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #import <WebKit/WebPluginDatabase.h>
31 #import <JavaScriptCore/Assertions.h>
32 #import <WebKit/WebBasePluginPackage.h>
33 #import <WebKit/WebDataSourcePrivate.h>
34 #import <WebKit/WebFrame.h>
35 #import <WebKit/WebFrameViewInternal.h>
36 #import <WebKit/WebHTMLRepresentation.h>
37 #import <WebKit/WebHTMLView.h>
38 #import <WebKit/WebKitLogging.h>
39 #import <WebKit/WebNetscapePluginPackage.h>
40 #import <WebKit/WebPluginPackage.h>
41 #import <WebKit/WebViewPrivate.h>
42 #import <WebKitSystemInterface.h>
44 static void checkCandidate(WebBasePluginPackage **currentPlugin, WebBasePluginPackage **candidatePlugin);
46 @interface WebPluginDatabase (Internal)
47 + (NSArray *)_defaultPlugInPaths;
48 - (NSArray *)_plugInPaths;
49 - (void)_addPlugin:(WebBasePluginPackage *)plugin;
50 - (void)_removePlugin:(WebBasePluginPackage *)plugin;
51 - (NSMutableSet *)_scanForNewPlugins;
54 @implementation WebPluginDatabase
56 static WebPluginDatabase *sharedDatabase = nil;
58 + (WebPluginDatabase *)sharedDatabase
60 if (!sharedDatabase) {
61 sharedDatabase = [[WebPluginDatabase alloc] init];
62 [sharedDatabase setPlugInPaths:[self _defaultPlugInPaths]];
63 [sharedDatabase refresh];
66 return sharedDatabase;
69 + (void)closeSharedDatabase
71 [sharedDatabase close];
74 static void checkCandidate(WebBasePluginPackage **currentPlugin, WebBasePluginPackage **candidatePlugin)
76 if (!*currentPlugin) {
77 *currentPlugin = *candidatePlugin;
81 if ([[[*currentPlugin bundle] bundleIdentifier] isEqualToString:[[*candidatePlugin bundle] bundleIdentifier]] && [*candidatePlugin versionNumber] > [*currentPlugin versionNumber])
82 *currentPlugin = *candidatePlugin;
85 - (WebBasePluginPackage *)pluginForKey:(NSString *)key withEnumeratorSelector:(SEL)enumeratorSelector
87 WebBasePluginPackage *plugin = nil;
88 WebBasePluginPackage *webPlugin = nil;
90 WebBasePluginPackage *CFMPlugin = nil;
91 WebBasePluginPackage *machoPlugin = nil;
94 NSEnumerator *pluginEnumerator = [plugins objectEnumerator];
95 key = [key lowercaseString];
97 while ((plugin = [pluginEnumerator nextObject]) != nil) {
98 if ([[[plugin performSelector:enumeratorSelector] allObjects] containsObject:key]) {
99 if ([plugin isKindOfClass:[WebPluginPackage class]])
100 checkCandidate(&webPlugin, &plugin);
102 else if([plugin isKindOfClass:[WebNetscapePluginPackage class]]) {
103 WebExecutableType executableType = [(WebNetscapePluginPackage *)plugin executableType];
104 if (executableType == WebCFMExecutableType) {
105 checkCandidate(&CFMPlugin, &plugin);
106 } else if (executableType == WebMachOExecutableType) {
107 checkCandidate(&machoPlugin, &plugin);
109 ASSERT_NOT_REACHED();
112 ASSERT_NOT_REACHED();
118 // Allow other plug-ins to win over QT because if the user has installed a plug-in that can handle a type
119 // that the QT plug-in can handle, they probably intended to override QT.
120 if (webPlugin && ![webPlugin isQuickTimePlugIn])
124 else if (machoPlugin && ![machoPlugin isQuickTimePlugIn])
126 else if (CFMPlugin && ![CFMPlugin isQuickTimePlugIn])
132 else if (machoPlugin)
140 - (WebBasePluginPackage *)pluginForMIMEType:(NSString *)MIMEType
142 return [self pluginForKey:[MIMEType lowercaseString]
143 withEnumeratorSelector:@selector(MIMETypeEnumerator)];
146 - (WebBasePluginPackage *)pluginForExtension:(NSString *)extension
148 WebBasePluginPackage *plugin = [self pluginForKey:[extension lowercaseString]
149 withEnumeratorSelector:@selector(extensionEnumerator)];
151 // If no plug-in was found from the extension, attempt to map from the extension to a MIME type
152 // and find the a plug-in from the MIME type. This is done in case the plug-in has not fully specified
153 // an extension <-> MIME type mapping.
154 NSString *MIMEType = WKGetMIMETypeForExtension(extension);
155 if ([MIMEType length] > 0)
156 plugin = [self pluginForMIMEType:MIMEType];
163 return [plugins allValues];
166 static NSArray *additionalWebPlugInPaths;
168 + (void)setAdditionalWebPlugInPaths:(NSArray *)additionalPaths
170 if (additionalPaths == additionalWebPlugInPaths)
173 [additionalWebPlugInPaths release];
174 additionalWebPlugInPaths = [additionalPaths copy];
176 // One might be tempted to add additionalWebPlugInPaths to the global WebPluginDatabase here.
177 // For backward compatibility with earlier versions of the +setAdditionalWebPlugInPaths: SPI,
178 // we need to save a copy of the additional paths and not cause a refresh of the plugin DB
180 // See Radars 4608487 and 4609047.
183 - (void)setPlugInPaths:(NSArray *)newPaths
185 if (plugInPaths == newPaths)
188 [plugInPaths release];
189 plugInPaths = [newPaths copy];
194 NSEnumerator *pluginEnumerator = [[self plugins] objectEnumerator];
195 WebBasePluginPackage *plugin;
196 while ((plugin = [pluginEnumerator nextObject]) != nil)
197 [self _removePlugin:plugin];
204 if (!(self = [super init]))
207 registeredMIMETypes = [[NSMutableSet alloc] init];
214 [plugInPaths release];
216 [registeredMIMETypes release];
223 // This method does a bit of autoreleasing, so create an autorelease pool to ensure that calling
224 // -refresh multiple times does not bloat the default pool.
225 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
227 // Create map from plug-in path to WebBasePluginPackage
229 plugins = [[NSMutableDictionary alloc] initWithCapacity:12];
231 // Find all plug-ins on disk
232 NSMutableSet *newPlugins = [self _scanForNewPlugins];
234 // Find plug-ins to remove from database (i.e., plug-ins that no longer exist on disk)
235 NSMutableSet *pluginsToRemove = [NSMutableSet set];
236 NSEnumerator *pluginEnumerator = [plugins objectEnumerator];
237 WebBasePluginPackage *plugin;
238 while ((plugin = [pluginEnumerator nextObject]) != nil) {
239 // Any plug-ins that were removed from disk since the last refresh should be removed from
241 if (![newPlugins containsObject:plugin])
242 [pluginsToRemove addObject:plugin];
244 // Remove every member of 'plugins' from 'newPlugins'. After this loop exits, 'newPlugins'
245 // will be the set of new plug-ins that should be added to the database.
246 [newPlugins removeObject:plugin];
250 if ([newPlugins count] > 0)
251 LOG(Plugins, "New plugins:\n%@", newPlugins);
252 if ([pluginsToRemove count] > 0)
253 LOG(Plugins, "Removed plugins:\n%@", pluginsToRemove);
256 // Remove plugins from database
257 pluginEnumerator = [pluginsToRemove objectEnumerator];
258 while ((plugin = [pluginEnumerator nextObject]) != nil)
259 [self _removePlugin:plugin];
261 // Add new plugins to database
262 pluginEnumerator = [newPlugins objectEnumerator];
263 while ((plugin = [pluginEnumerator nextObject]) != nil)
264 [self _addPlugin:plugin];
266 // Build a list of MIME types.
267 NSMutableSet *MIMETypes = [[NSMutableSet alloc] init];
268 pluginEnumerator = [plugins objectEnumerator];
269 while ((plugin = [pluginEnumerator nextObject]) != nil)
270 [MIMETypes addObjectsFromArray:[[plugin MIMETypeEnumerator] allObjects]];
272 // Register plug-in views and representations.
273 NSEnumerator *MIMEEnumerator = [MIMETypes objectEnumerator];
275 while ((MIMEType = [MIMEEnumerator nextObject]) != nil) {
276 [registeredMIMETypes addObject:MIMEType];
278 if ([WebView canShowMIMETypeAsHTML:MIMEType])
279 // Don't allow plug-ins to override our core HTML types.
281 plugin = [self pluginForMIMEType:MIMEType];
282 if ([plugin isJavaPlugIn])
283 // Don't register the Java plug-in for a document view since Java files should be downloaded when not embedded.
285 if ([plugin isQuickTimePlugIn] && [[WebFrameView _viewTypesAllowImageTypeOmission:NO] objectForKey:MIMEType])
286 // Don't allow the QT plug-in to override any types because it claims many that we can handle ourselves.
289 if (self == sharedDatabase)
290 [WebView registerViewClass:[WebHTMLView class] representationClass:[WebHTMLRepresentation class] forMIMEType:MIMEType];
297 - (BOOL)isMIMETypeRegistered:(NSString *)MIMEType
299 return [registeredMIMETypes containsObject:MIMEType];
304 @implementation WebPluginDatabase (Internal)
306 + (NSArray *)_defaultPlugInPaths
308 // Plug-ins are found in order of precedence.
309 // If there are duplicates, the first found plug-in is used.
310 // For example, if there is a QuickTime.plugin in the users's home directory
311 // that is used instead of the /Library/Internet Plug-ins version.
312 // The purpose is to allow non-admin users to update their plug-ins.
313 return [NSArray arrayWithObjects:
314 [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Internet Plug-Ins"],
315 @"/Library/Internet Plug-Ins",
316 [[NSBundle mainBundle] builtInPlugInsPath],
320 - (NSArray *)_plugInPaths
322 if (self == sharedDatabase && additionalWebPlugInPaths) {
323 // Add additionalWebPlugInPaths to the global WebPluginDatabase. We do this here for
324 // backward compatibility with earlier versions of the +setAdditionalWebPlugInPaths: SPI,
325 // which simply saved a copy of the additional paths and did not cause the plugin DB to
326 // refresh. See Radars 4608487 and 4609047.
327 NSMutableArray *modifiedPlugInPaths = [[plugInPaths mutableCopy] autorelease];
328 [modifiedPlugInPaths addObjectsFromArray:additionalWebPlugInPaths];
329 return modifiedPlugInPaths;
334 - (void)_addPlugin:(WebBasePluginPackage *)plugin
337 NSString *pluginPath = [plugin path];
339 [plugins setObject:plugin forKey:pluginPath];
340 [plugin wasAddedToPluginDatabase:self];
343 - (void)_removePlugin:(WebBasePluginPackage *)plugin
347 // Unregister plug-in's MIME type registrations
348 NSEnumerator *MIMETypeEnumerator = [plugin MIMETypeEnumerator];
350 while ((MIMEType = [MIMETypeEnumerator nextObject])) {
351 if ([registeredMIMETypes containsObject:MIMEType]) {
352 if (self == sharedDatabase)
353 [WebView _unregisterViewClassAndRepresentationClassForMIMEType:MIMEType];
354 [registeredMIMETypes removeObject:MIMEType];
358 // Remove plug-in from database
359 NSString *pluginPath = [plugin path];
362 [plugins removeObjectForKey:pluginPath];
363 [plugin wasRemovedFromPluginDatabase:self];
367 - (NSMutableSet *)_scanForNewPlugins
369 NSMutableSet *newPlugins = [NSMutableSet set];
370 NSEnumerator *directoryEnumerator = [[self _plugInPaths] objectEnumerator];
371 NSMutableSet *uniqueFilenames = [[NSMutableSet alloc] init];
372 NSFileManager *fileManager = [NSFileManager defaultManager];
373 NSString *pluginDirectory;
374 while ((pluginDirectory = [directoryEnumerator nextObject]) != nil) {
375 // Get contents of each plug-in directory
376 NSEnumerator *filenameEnumerator = [[fileManager directoryContentsAtPath:pluginDirectory] objectEnumerator];
378 while ((filename = [filenameEnumerator nextObject]) != nil) {
379 // Unique plug-ins by filename
380 if ([uniqueFilenames containsObject:filename])
382 [uniqueFilenames addObject:filename];
384 // Create a plug-in package for this path
385 NSString *pluginPath = [pluginDirectory stringByAppendingPathComponent:filename];
386 WebBasePluginPackage *pluginPackage = [plugins objectForKey:pluginPath];
388 pluginPackage = [WebBasePluginPackage pluginWithPath:pluginPath];
390 [newPlugins addObject:pluginPackage];
393 [uniqueFilenames release];