• 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 "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>
47
48using namespace WebCore;
49
50static void checkCandidate(WebBasePluginPackage **currentPlugin, WebBasePluginPackage **candidatePlugin);
51
52@interface WebPluginDatabase (Internal)
53+ (NSArray *)_defaultPlugInPaths;
54- (NSArray *)_plugInPaths;
55- (void)_addPlugin:(WebBasePluginPackage *)plugin;
56- (void)_removePlugin:(WebBasePluginPackage *)plugin;
57- (NSMutableSet *)_scanForNewPlugins;
58@end
59
60@implementation WebPluginDatabase
61
62static WebPluginDatabase *sharedDatabase = nil;
63
64+ (WebPluginDatabase *)sharedDatabase
65{
66    if (!sharedDatabase) {
67        sharedDatabase = [[WebPluginDatabase alloc] init];
68        [sharedDatabase setPlugInPaths:[self _defaultPlugInPaths]];
69        [sharedDatabase refresh];
70    }
71
72    return sharedDatabase;
73}
74
75+ (void)closeSharedDatabase
76{
77    [sharedDatabase close];
78}
79
80static void checkCandidate(WebBasePluginPackage **currentPlugin, WebBasePluginPackage **candidatePlugin)
81{
82    if (!*currentPlugin) {
83        *currentPlugin = *candidatePlugin;
84        return;
85    }
86
87    if ([*currentPlugin bundleIdentifier] == [*candidatePlugin bundleIdentifier] && [*candidatePlugin versionNumber] > [*currentPlugin versionNumber])
88        *currentPlugin = *candidatePlugin;
89}
90
91struct PluginPackageCandidates {
92    PluginPackageCandidates()
93        : webPlugin(nil)
94        , machoPlugin(nil)
95#ifdef SUPPORT_CFM
96        , CFMPlugin(nil)
97#endif
98    {
99    }
100
101    void update(WebBasePluginPackage *plugin)
102    {
103        if ([plugin isKindOfClass:[WebPluginPackage class]]) {
104            checkCandidate(&webPlugin, &plugin);
105            return;
106        }
107
108#if ENABLE(NETSCAPE_PLUGIN_API)
109        if([plugin isKindOfClass:[WebNetscapePluginPackage class]]) {
110            WebExecutableType executableType = [(WebNetscapePluginPackage *)plugin executableType];
111#ifdef SUPPORT_CFM
112            if (executableType == WebCFMExecutableType) {
113                checkCandidate(&CFMPlugin, &plugin);
114                return;
115            }
116#endif // SUPPORT_CFM
117            if (executableType == WebMachOExecutableType) {
118                checkCandidate(&machoPlugin, &plugin);
119                return;
120            }
121        }
122#endif
123        ASSERT_NOT_REACHED();
124    }
125
126    WebBasePluginPackage *bestCandidate()
127    {
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])
131            return webPlugin;
132
133        if (machoPlugin && ![machoPlugin isQuickTimePlugIn])
134            return machoPlugin;
135
136#ifdef SUPPORT_CFM
137        if (CFMPlugin && ![CFMPlugin isQuickTimePlugIn])
138            return CFMPlugin;
139#endif // SUPPORT_CFM
140
141        if (webPlugin)
142            return webPlugin;
143        if (machoPlugin)
144            return machoPlugin;
145#ifdef SUPPORT_CFM
146        if (CFMPlugin)
147            return CFMPlugin;
148#endif
149        return nil;
150    }
151
152    WebBasePluginPackage *webPlugin;
153    WebBasePluginPackage *machoPlugin;
154#ifdef SUPPORT_CFM
155    WebBasePluginPackage *CFMPlugin;
156#endif
157};
158
159- (WebBasePluginPackage *)pluginForMIMEType:(NSString *)MIMEType
160{
161    PluginPackageCandidates candidates;
162
163    MIMEType = [MIMEType lowercaseString];
164    NSEnumerator *pluginEnumerator = [plugins objectEnumerator];
165
166    while (WebBasePluginPackage *plugin = [pluginEnumerator nextObject]) {
167        if ([plugin supportsMIMEType:MIMEType])
168            candidates.update(plugin);
169    }
170
171    return candidates.bestCandidate();
172}
173
174- (WebBasePluginPackage *)pluginForExtension:(NSString *)extension
175{
176    PluginPackageCandidates candidates;
177
178    extension = [extension lowercaseString];
179    NSEnumerator *pluginEnumerator = [plugins objectEnumerator];
180
181    while (WebBasePluginPackage *plugin = [pluginEnumerator nextObject]) {
182        if ([plugin supportsExtension:extension])
183            candidates.update(plugin);
184    }
185
186    WebBasePluginPackage *plugin = candidates.bestCandidate();
187
188    if (!plugin) {
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];
195    }
196    return plugin;
197}
198
199- (NSArray *)plugins
200{
201    return [plugins allValues];
202}
203
204static NSArray *additionalWebPlugInPaths;
205
206+ (void)setAdditionalWebPlugInPaths:(NSArray *)additionalPaths
207{
208    if (additionalPaths == additionalWebPlugInPaths)
209        return;
210
211    [additionalWebPlugInPaths release];
212    additionalWebPlugInPaths = [additionalPaths copy];
213
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
217    // at this time.
218    // See Radars 4608487 and 4609047.
219}
220
221- (void)setPlugInPaths:(NSArray *)newPaths
222{
223    if (plugInPaths == newPaths)
224        return;
225
226    [plugInPaths release];
227    plugInPaths = [newPaths copy];
228}
229
230- (void)close
231{
232    NSEnumerator *pluginEnumerator = [[self plugins] objectEnumerator];
233    WebBasePluginPackage *plugin;
234    while ((plugin = [pluginEnumerator nextObject]) != nil)
235        [self _removePlugin:plugin];
236    [plugins release];
237    plugins = nil;
238}
239
240- (id)init
241{
242    if (!(self = [super init]))
243        return nil;
244
245    registeredMIMETypes = [[NSMutableSet alloc] init];
246    pluginInstanceViews = [[NSMutableSet alloc] init];
247
248    return self;
249}
250
251- (void)dealloc
252{
253    [plugInPaths release];
254    [plugins release];
255    [registeredMIMETypes release];
256    [pluginInstanceViews release];
257
258    [super dealloc];
259}
260
261- (void)refresh
262{
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];
266
267    // Create map from plug-in path to WebBasePluginPackage
268    if (!plugins)
269        plugins = [[NSMutableDictionary alloc] initWithCapacity:12];
270
271    // Find all plug-ins on disk
272    NSMutableSet *newPlugins = [self _scanForNewPlugins];
273
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
280        // the database.
281        if (![newPlugins containsObject:plugin])
282            [pluginsToRemove addObject:plugin];
283
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];
287    }
288
289#if !LOG_DISABLED
290    if ([newPlugins count] > 0)
291        LOG(Plugins, "New plugins:\n%@", newPlugins);
292    if ([pluginsToRemove count] > 0)
293        LOG(Plugins, "Removed plugins:\n%@", pluginsToRemove);
294#endif
295
296    // Remove plugins from database
297    pluginEnumerator = [pluginsToRemove objectEnumerator];
298    while ((plugin = [pluginEnumerator nextObject]) != nil)
299        [self _removePlugin:plugin];
300
301    // Add new plugins to database
302    pluginEnumerator = [newPlugins objectEnumerator];
303    while ((plugin = [pluginEnumerator nextObject]) != nil)
304        [self _addPlugin:plugin];
305
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];
313    }
314
315    // Register plug-in views and representations.
316    NSEnumerator *MIMEEnumerator = [MIMETypes objectEnumerator];
317    NSString *MIMEType;
318    while ((MIMEType = [MIMEEnumerator nextObject]) != nil) {
319        [registeredMIMETypes addObject:MIMEType];
320
321        if ([WebView canShowMIMETypeAsHTML:MIMEType])
322            // Don't allow plug-ins to override our core HTML types.
323            continue;
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.
327            continue;
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.
330            continue;
331
332        if (self == sharedDatabase)
333            [WebView _registerPluginMIMEType:MIMEType];
334    }
335    [MIMETypes release];
336
337    [pool drain];
338}
339
340- (BOOL)isMIMETypeRegistered:(NSString *)MIMEType
341{
342    return [registeredMIMETypes containsObject:MIMEType];
343}
344
345- (void)addPluginInstanceView:(NSView *)view
346{
347    [pluginInstanceViews addObject:view];
348}
349
350- (void)removePluginInstanceView:(NSView *)view
351{
352    [pluginInstanceViews removeObject:view];
353}
354
355- (void)removePluginInstanceViewsFor:(WebFrame*)webFrame
356{
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
358
359    if( [pluginInstanceViews count] == 0 )
360        return;
361
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;
367
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])
372#else
373            if ([WebPluginController isPlugInView:subview])
374#endif
375                [pluginInstanceViews removeObject:subview];
376        }
377    }
378}
379
380- (void)destroyAllPluginInstanceViews
381{
382    NSView *view;
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)];
390        } else
391#endif
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)];
397        }
398    }
399}
400
401@end
402
403@implementation WebPluginDatabase (Internal)
404
405+ (NSArray *)_defaultPlugInPaths
406{
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],
416        nil];
417}
418
419- (NSArray *)_plugInPaths
420{
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;
429    } else
430        return plugInPaths;
431}
432
433- (void)_addPlugin:(WebBasePluginPackage *)plugin
434{
435    ASSERT(plugin);
436    NSString *pluginPath = [plugin path];
437    ASSERT(pluginPath);
438    [plugins setObject:plugin forKey:pluginPath];
439    [plugin wasAddedToPluginDatabase:self];
440}
441
442- (void)_removePlugin:(WebBasePluginPackage *)plugin
443{
444    ASSERT(plugin);
445
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;
450
451        if ([registeredMIMETypes containsObject:MIMEType]) {
452            if (self == sharedDatabase)
453                [WebView _unregisterPluginMIMEType:MIMEType];
454            [registeredMIMETypes removeObject:MIMEType];
455        }
456    }
457
458    // Remove plug-in from database
459    NSString *pluginPath = [plugin path];
460    ASSERT(pluginPath);
461    [plugin retain];
462    [plugins removeObjectForKey:pluginPath];
463    [plugin wasRemovedFromPluginDatabase:self];
464    [plugin release];
465}
466
467- (NSMutableSet *)_scanForNewPlugins
468{
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];
477        NSString *filename;
478        while ((filename = [filenameEnumerator nextObject]) != nil) {
479            // Unique plug-ins by filename
480            if ([uniqueFilenames containsObject:filename])
481                continue;
482            [uniqueFilenames addObject:filename];
483
484            // Create a plug-in package for this path
485            NSString *pluginPath = [pluginDirectory stringByAppendingPathComponent:filename];
486            WebBasePluginPackage *pluginPackage = [plugins objectForKey:pluginPath];
487            if (!pluginPackage)
488                pluginPackage = [WebBasePluginPackage pluginWithPath:pluginPath];
489            if (pluginPackage)
490                [newPlugins addObject:pluginPackage];
491        }
492    }
493    [uniqueFilenames release];
494
495    return newPlugins;
496}
497
498@end
499