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 "WebPluginDatabase.h"
31 #import "WebBaseNetscapePluginView.h"
32 #import "WebBasePluginPackage.h"
33 #import "WebDataSourcePrivate.h"
35 #import "WebFrameViewInternal.h"
36 #import "WebHTMLRepresentation.h"
37 #import "WebHTMLView.h"
38 #import "WebKitLogging.h"
39 #import "WebNSFileManagerExtras.h"
40 #import "WebNetscapePluginPackage.h"
41 #import "WebPluginController.h"
42 #import "WebPluginPackage.h"
43 #import "WebViewPrivate.h"
44 #import "WebViewInternal.h"
45 #import <WebKitSystemInterface.h>
46 #import <wtf/Assertions.h>
48 using namespace WebCore;
50 static void checkCandidate(WebBasePluginPackage **currentPlugin, WebBasePluginPackage **candidatePlugin);
52 @interface WebPluginDatabase (Internal)
53 + (NSArray *)_defaultPlugInPaths;
54 - (NSArray *)_plugInPaths;
55 - (void)_addPlugin:(WebBasePluginPackage *)plugin;
56 - (void)_removePlugin:(WebBasePluginPackage *)plugin;
57 - (NSMutableSet *)_scanForNewPlugins;
60 @implementation WebPluginDatabase
62 static WebPluginDatabase *sharedDatabase = nil;
64 + (WebPluginDatabase *)sharedDatabase
66 if (!sharedDatabase) {
67 sharedDatabase = [[WebPluginDatabase alloc] init];
68 [sharedDatabase setPlugInPaths:[self _defaultPlugInPaths]];
69 [sharedDatabase refresh];
72 return sharedDatabase;
75 + (void)closeSharedDatabase
77 [sharedDatabase close];
80 static void checkCandidate(WebBasePluginPackage **currentPlugin, WebBasePluginPackage **candidatePlugin)
82 if (!*currentPlugin) {
83 *currentPlugin = *candidatePlugin;
87 if ([*currentPlugin bundleIdentifier] == [*candidatePlugin bundleIdentifier] && [*candidatePlugin versionNumber] > [*currentPlugin versionNumber])
88 *currentPlugin = *candidatePlugin;
91 struct PluginPackageCandidates {
92 PluginPackageCandidates()
101 void update(WebBasePluginPackage *plugin)
103 if ([plugin isKindOfClass:[WebPluginPackage class]]) {
104 checkCandidate(&webPlugin, &plugin);
108 #if ENABLE(NETSCAPE_PLUGIN_API)
109 if([plugin isKindOfClass:[WebNetscapePluginPackage class]]) {
110 WebExecutableType executableType = [(WebNetscapePluginPackage *)plugin executableType];
112 if (executableType == WebCFMExecutableType) {
113 checkCandidate(&CFMPlugin, &plugin);
116 #endif // SUPPORT_CFM
117 if (executableType == WebMachOExecutableType) {
118 checkCandidate(&machoPlugin, &plugin);
123 ASSERT_NOT_REACHED();
126 WebBasePluginPackage *bestCandidate()
128 // Allow other plug-ins to win over QT because if the user has installed a plug-in that can handle a type
129 // that the QT plug-in can handle, they probably intended to override QT.
130 if (webPlugin && ![webPlugin isQuickTimePlugIn])
133 if (machoPlugin && ![machoPlugin isQuickTimePlugIn])
137 if (CFMPlugin && ![CFMPlugin isQuickTimePlugIn])
139 #endif // SUPPORT_CFM
152 WebBasePluginPackage *webPlugin;
153 WebBasePluginPackage *machoPlugin;
155 WebBasePluginPackage *CFMPlugin;
159 - (WebBasePluginPackage *)pluginForMIMEType:(NSString *)MIMEType
161 PluginPackageCandidates candidates;
163 MIMEType = [MIMEType lowercaseString];
164 NSEnumerator *pluginEnumerator = [plugins objectEnumerator];
166 while (WebBasePluginPackage *plugin = [pluginEnumerator nextObject]) {
167 if ([plugin supportsMIMEType:MIMEType])
168 candidates.update(plugin);
171 return candidates.bestCandidate();
174 - (WebBasePluginPackage *)pluginForExtension:(NSString *)extension
176 PluginPackageCandidates candidates;
178 extension = [extension lowercaseString];
179 NSEnumerator *pluginEnumerator = [plugins objectEnumerator];
181 while (WebBasePluginPackage *plugin = [pluginEnumerator nextObject]) {
182 if ([plugin supportsExtension:extension])
183 candidates.update(plugin);
186 WebBasePluginPackage *plugin = candidates.bestCandidate();
189 // If no plug-in was found from the extension, attempt to map from the extension to a MIME type
190 // and find the a plug-in from the MIME type. This is done in case the plug-in has not fully specified
191 // an extension <-> MIME type mapping.
192 NSString *MIMEType = WKGetMIMETypeForExtension(extension);
193 if ([MIMEType length] > 0)
194 plugin = [self pluginForMIMEType:MIMEType];
201 return [plugins allValues];
204 static NSArray *additionalWebPlugInPaths;
206 + (void)setAdditionalWebPlugInPaths:(NSArray *)additionalPaths
208 if (additionalPaths == additionalWebPlugInPaths)
211 [additionalWebPlugInPaths release];
212 additionalWebPlugInPaths = [additionalPaths copy];
214 // One might be tempted to add additionalWebPlugInPaths to the global WebPluginDatabase here.
215 // For backward compatibility with earlier versions of the +setAdditionalWebPlugInPaths: SPI,
216 // we need to save a copy of the additional paths and not cause a refresh of the plugin DB
218 // See Radars 4608487 and 4609047.
221 - (void)setPlugInPaths:(NSArray *)newPaths
223 if (plugInPaths == newPaths)
226 [plugInPaths release];
227 plugInPaths = [newPaths copy];
232 NSEnumerator *pluginEnumerator = [[self plugins] objectEnumerator];
233 WebBasePluginPackage *plugin;
234 while ((plugin = [pluginEnumerator nextObject]) != nil)
235 [self _removePlugin:plugin];
242 if (!(self = [super init]))
245 registeredMIMETypes = [[NSMutableSet alloc] init];
246 pluginInstanceViews = [[NSMutableSet alloc] init];
253 [plugInPaths release];
255 [registeredMIMETypes release];
256 [pluginInstanceViews release];
263 // This method does a bit of autoreleasing, so create an autorelease pool to ensure that calling
264 // -refresh multiple times does not bloat the default pool.
265 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
267 // Create map from plug-in path to WebBasePluginPackage
269 plugins = [[NSMutableDictionary alloc] initWithCapacity:12];
271 // Find all plug-ins on disk
272 NSMutableSet *newPlugins = [self _scanForNewPlugins];
274 // Find plug-ins to remove from database (i.e., plug-ins that no longer exist on disk)
275 NSMutableSet *pluginsToRemove = [NSMutableSet set];
276 NSEnumerator *pluginEnumerator = [plugins objectEnumerator];
277 WebBasePluginPackage *plugin;
278 while ((plugin = [pluginEnumerator nextObject]) != nil) {
279 // Any plug-ins that were removed from disk since the last refresh should be removed from
281 if (![newPlugins containsObject:plugin])
282 [pluginsToRemove addObject:plugin];
284 // Remove every member of 'plugins' from 'newPlugins'. After this loop exits, 'newPlugins'
285 // will be the set of new plug-ins that should be added to the database.
286 [newPlugins removeObject:plugin];
290 if ([newPlugins count] > 0)
291 LOG(Plugins, "New plugins:\n%@", newPlugins);
292 if ([pluginsToRemove count] > 0)
293 LOG(Plugins, "Removed plugins:\n%@", pluginsToRemove);
296 // Remove plugins from database
297 pluginEnumerator = [pluginsToRemove objectEnumerator];
298 while ((plugin = [pluginEnumerator nextObject]) != nil)
299 [self _removePlugin:plugin];
301 // Add new plugins to database
302 pluginEnumerator = [newPlugins objectEnumerator];
303 while ((plugin = [pluginEnumerator nextObject]) != nil)
304 [self _addPlugin:plugin];
306 // Build a list of MIME types.
307 NSMutableSet *MIMETypes = [[NSMutableSet alloc] init];
308 pluginEnumerator = [plugins objectEnumerator];
309 while ((plugin = [pluginEnumerator nextObject])) {
310 const PluginInfo& pluginInfo = [plugin pluginInfo];
311 for (size_t i = 0; i < pluginInfo.mimes.size(); ++i)
312 [MIMETypes addObject:pluginInfo.mimes[i].type];
315 // Register plug-in views and representations.
316 NSEnumerator *MIMEEnumerator = [MIMETypes objectEnumerator];
318 while ((MIMEType = [MIMEEnumerator nextObject]) != nil) {
319 [registeredMIMETypes addObject:MIMEType];
321 if ([WebView canShowMIMETypeAsHTML:MIMEType])
322 // Don't allow plug-ins to override our core HTML types.
324 plugin = [self pluginForMIMEType:MIMEType];
325 if ([plugin isJavaPlugIn])
326 // Don't register the Java plug-in for a document view since Java files should be downloaded when not embedded.
328 if ([plugin isQuickTimePlugIn] && [[WebFrameView _viewTypesAllowImageTypeOmission:NO] objectForKey:MIMEType])
329 // Don't allow the QT plug-in to override any types because it claims many that we can handle ourselves.
332 if (self == sharedDatabase)
333 [WebView _registerPluginMIMEType:MIMEType];
340 - (BOOL)isMIMETypeRegistered:(NSString *)MIMEType
342 return [registeredMIMETypes containsObject:MIMEType];
345 - (void)addPluginInstanceView:(NSView *)view
347 [pluginInstanceViews addObject:view];
350 - (void)removePluginInstanceView:(NSView *)view
352 [pluginInstanceViews removeObject:view];
355 - (void)removePluginInstanceViewsFor:(WebFrame*)webFrame
357 // This handles handles the case where a frame or view is being destroyed and the plugin needs to be removed from the list first
359 if( [pluginInstanceViews count] == 0 )
362 NSView <WebDocumentView> *documentView = [[webFrame frameView] documentView];
363 if ([documentView isKindOfClass:[WebHTMLView class]]) {
364 NSArray *subviews = [documentView subviews];
365 unsigned int subviewCount = [subviews count];
366 unsigned int subviewIndex;
368 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) {
369 NSView *subview = [subviews objectAtIndex:subviewIndex];
370 #if ENABLE(NETSCAPE_PLUGIN_API)
371 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]] || [WebPluginController isPlugInView:subview])
373 if ([WebPluginController isPlugInView:subview])
375 [pluginInstanceViews removeObject:subview];
380 - (void)destroyAllPluginInstanceViews
383 NSArray *pli = [pluginInstanceViews allObjects];
384 NSEnumerator *enumerator = [pli objectEnumerator];
385 while ((view = [enumerator nextObject]) != nil) {
386 #if ENABLE(NETSCAPE_PLUGIN_API)
387 if ([view isKindOfClass:[WebBaseNetscapePluginView class]]) {
388 ASSERT([view respondsToSelector:@selector(stop)]);
389 [view performSelector:@selector(stop)];
392 if ([WebPluginController isPlugInView:view]) {
393 ASSERT([[view superview] isKindOfClass:[WebHTMLView class]]);
394 ASSERT([[view superview] respondsToSelector:@selector(_destroyAllWebPlugins)]);
395 // this will actually destroy all plugin instances for a webHTMLView and remove them from this list
396 [[view superview] performSelector:@selector(_destroyAllWebPlugins)];
403 @implementation WebPluginDatabase (Internal)
405 + (NSArray *)_defaultPlugInPaths
407 // Plug-ins are found in order of precedence.
408 // If there are duplicates, the first found plug-in is used.
409 // For example, if there is a QuickTime.plugin in the users's home directory
410 // that is used instead of the /Library/Internet Plug-ins version.
411 // The purpose is to allow non-admin users to update their plug-ins.
412 return [NSArray arrayWithObjects:
413 [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Internet Plug-Ins"],
414 @"/Library/Internet Plug-Ins",
415 [[NSBundle mainBundle] builtInPlugInsPath],
419 - (NSArray *)_plugInPaths
421 if (self == sharedDatabase && additionalWebPlugInPaths) {
422 // Add additionalWebPlugInPaths to the global WebPluginDatabase. We do this here for
423 // backward compatibility with earlier versions of the +setAdditionalWebPlugInPaths: SPI,
424 // which simply saved a copy of the additional paths and did not cause the plugin DB to
425 // refresh. See Radars 4608487 and 4609047.
426 NSMutableArray *modifiedPlugInPaths = [[plugInPaths mutableCopy] autorelease];
427 [modifiedPlugInPaths addObjectsFromArray:additionalWebPlugInPaths];
428 return modifiedPlugInPaths;
433 - (void)_addPlugin:(WebBasePluginPackage *)plugin
436 NSString *pluginPath = [plugin path];
438 [plugins setObject:plugin forKey:pluginPath];
439 [plugin wasAddedToPluginDatabase:self];
442 - (void)_removePlugin:(WebBasePluginPackage *)plugin
446 // Unregister plug-in's MIME type registrations
447 const PluginInfo& pluginInfo = [plugin pluginInfo];
448 for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) {
449 NSString *MIMEType = pluginInfo.mimes[i].type;
451 if ([registeredMIMETypes containsObject:MIMEType]) {
452 if (self == sharedDatabase)
453 [WebView _unregisterPluginMIMEType:MIMEType];
454 [registeredMIMETypes removeObject:MIMEType];
458 // Remove plug-in from database
459 NSString *pluginPath = [plugin path];
462 [plugins removeObjectForKey:pluginPath];
463 [plugin wasRemovedFromPluginDatabase:self];
467 - (NSMutableSet *)_scanForNewPlugins
469 NSMutableSet *newPlugins = [NSMutableSet set];
470 NSEnumerator *directoryEnumerator = [[self _plugInPaths] objectEnumerator];
471 NSMutableSet *uniqueFilenames = [[NSMutableSet alloc] init];
472 NSFileManager *fileManager = [NSFileManager defaultManager];
473 NSString *pluginDirectory;
474 while ((pluginDirectory = [directoryEnumerator nextObject]) != nil) {
475 // Get contents of each plug-in directory
476 NSEnumerator *filenameEnumerator = [[fileManager contentsOfDirectoryAtPath:pluginDirectory error:NULL] objectEnumerator];
478 while ((filename = [filenameEnumerator nextObject]) != nil) {
479 // Unique plug-ins by filename
480 if ([uniqueFilenames containsObject:filename])
482 [uniqueFilenames addObject:filename];
484 // Create a plug-in package for this path
485 NSString *pluginPath = [pluginDirectory stringByAppendingPathComponent:filename];
486 WebBasePluginPackage *pluginPackage = [plugins objectForKey:pluginPath];
488 pluginPackage = [WebBasePluginPackage pluginWithPath:pluginPath];
490 [newPlugins addObject:pluginPackage];
493 [uniqueFilenames release];