• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
48static 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
60static 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
78static 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
173static 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