2006-04-28 Eric Seidel <eseidel@apple.com>
[WebKit-https.git] / WebKit / Plugins / WebPluginDatabase.m
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 <WebKit/WebPluginDatabase.h>
30
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/WebKitLogging.h>
37 #import <WebKit/WebNetscapePluginDocumentView.h>
38 #import <WebKit/WebNetscapePluginPackage.h>
39 #import <WebKit/WebNetscapePluginRepresentation.h>
40 #import <WebKit/WebPluginDocumentView.h>
41 #import <WebKit/WebPluginPackage.h>
42 #import <WebKit/WebViewPrivate.h>
43 #import <WebKitSystemInterface.h>
44
45 @implementation WebPluginDatabase
46
47 static WebPluginDatabase *database = nil;
48
49 + (WebPluginDatabase *)installedPlugins 
50 {
51     if (!database)
52         database = [[WebPluginDatabase alloc] init];
53     return database;
54 }
55
56 - (WebBasePluginPackage *)pluginForKey:(NSString *)key withEnumeratorSelector:(SEL)enumeratorSelector
57 {
58     WebBasePluginPackage *plugin, *CFMPlugin=nil, *machoPlugin=nil, *webPlugin=nil;
59     NSEnumerator *pluginEnumerator = [plugins objectEnumerator];
60     key = [key lowercaseString];
61
62     while ((plugin = [pluginEnumerator nextObject]) != nil) {
63         if ([[[plugin performSelector:enumeratorSelector] allObjects] containsObject:key]) {
64             if ([plugin isKindOfClass:[WebPluginPackage class]]) {
65                 if (!webPlugin)
66                     webPlugin = plugin;
67             } else if([plugin isKindOfClass:[WebNetscapePluginPackage class]]) {
68                 WebExecutableType executableType = [(WebNetscapePluginPackage *)plugin executableType];
69                 if (executableType == WebCFMExecutableType) {
70                     if (!CFMPlugin)
71                         CFMPlugin = plugin;
72                 } else if (executableType == WebMachOExecutableType) {
73                     if (!machoPlugin)
74                         machoPlugin = plugin;
75                 } else {
76                     ASSERT_NOT_REACHED();
77                 }
78             } else {
79                 ASSERT_NOT_REACHED();
80             }
81         }
82     }
83
84     // Allow other plug-ins to win over QT because if the user has installed a plug-in that can handle a type
85     // that the QT plug-in can handle, they probably intended to override QT.
86     if (webPlugin && ![webPlugin isQuickTimePlugIn])
87         return webPlugin;
88     else if (machoPlugin && ![machoPlugin isQuickTimePlugIn])
89         return machoPlugin;
90     else if (CFMPlugin && ![CFMPlugin isQuickTimePlugIn])
91         return CFMPlugin;
92     else if (webPlugin)
93         return webPlugin;
94     else if (machoPlugin)
95         return machoPlugin;
96     else if (CFMPlugin)
97         return CFMPlugin;
98     return nil;
99 }
100
101 - (WebBasePluginPackage *)pluginForMIMEType:(NSString *)MIMEType
102 {
103     return [self pluginForKey:[MIMEType lowercaseString]
104        withEnumeratorSelector:@selector(MIMETypeEnumerator)];
105 }
106
107 - (WebBasePluginPackage *)pluginForExtension:(NSString *)extension
108 {
109     WebBasePluginPackage *plugin = [self pluginForKey:[extension lowercaseString]
110                                withEnumeratorSelector:@selector(extensionEnumerator)];
111     if (!plugin) {
112         // If no plug-in was found from the extension, attempt to map from the extension to a MIME type
113         // and find the a plug-in from the MIME type. This is done in case the plug-in has not fully specified
114         // an extension <-> MIME type mapping.
115         NSString *MIMEType = WKGetMIMETypeForExtension(extension);
116         if ([MIMEType length] > 0)
117             plugin = [self pluginForMIMEType:MIMEType];
118     }
119     return plugin;
120 }
121
122 - (NSArray *)plugins
123 {
124     return [plugins allObjects];
125 }
126
127 static NSArray *extensionPlugInPaths;
128
129 + (void)setAdditionalWebPlugInPaths:(NSArray *)a
130 {
131     if (a != extensionPlugInPaths) {
132         [extensionPlugInPaths release];
133         extensionPlugInPaths = [a copyWithZone:nil];
134     }
135 }
136
137 static NSArray *pluginLocations(void)
138 {
139     // Plug-ins are found in order of precedence.
140     // If there are duplicates, the first found plug-in is used.
141     // For example, if there is a QuickTime.plugin in the users's home directory
142     // that is used instead of the /Library/Internet Plug-ins version.
143     // The purpose is to allow non-admin users to update their plug-ins.
144     NSMutableArray *array = [NSMutableArray arrayWithCapacity:[extensionPlugInPaths count] + 3];
145     
146     if (extensionPlugInPaths)
147         [array addObjectsFromArray:extensionPlugInPaths];
148     
149     [array addObject:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Internet Plug-Ins"]];
150     [array addObject:@"/Library/Internet Plug-Ins"];
151     [array addObject:[[NSBundle mainBundle] builtInPlugInsPath]];
152         
153     return array;
154 }
155
156 - (id)init
157 {
158     self = [super init];
159     [self refresh];
160     return self;
161 }
162     
163 - (void)refresh
164 {
165     NSEnumerator *directoryEnumerator = [pluginLocations() objectEnumerator];
166     NSMutableSet *uniqueFilenames = [[NSMutableArray alloc] init];
167     NSFileManager *fileManager = [NSFileManager defaultManager];
168     NSMutableSet *newPlugins = [[NSMutableSet alloc] init];
169     NSString *pluginDirectory;
170     
171     // Create a new set of plug-ins.
172     while ((pluginDirectory = [directoryEnumerator nextObject]) != nil) {
173         NSEnumerator *filenameEnumerator = [[fileManager directoryContentsAtPath:pluginDirectory] objectEnumerator];
174         NSString *filename;
175         while ((filename = [filenameEnumerator nextObject]) != nil) {
176             if (![uniqueFilenames containsObject:filename]) {
177                 [uniqueFilenames addObject:filename];
178                 NSString *pluginPath = [pluginDirectory stringByAppendingPathComponent:filename];
179                 WebBasePluginPackage *pluginPackage = [WebBasePluginPackage pluginWithPath:pluginPath];
180                 if (pluginPackage) {
181                     [newPlugins addObject:pluginPackage];
182                 }
183             }
184         }
185     }
186     
187     [uniqueFilenames release];
188     
189     //  Remove all uninstalled plug-ins and add the new plug-ins.
190     if (plugins) {
191         NSMutableSet *pluginsToUnload = [plugins mutableCopy];
192         [pluginsToUnload minusSet:newPlugins];
193 #if !LOG_DISABLED
194         NSMutableSet *reallyNewPlugins = [newPlugins mutableCopy];
195         [reallyNewPlugins minusSet:plugins];
196         if ([reallyNewPlugins count] > 0) {
197             LOG(Plugins, "New plugins:\n%@", reallyNewPlugins);
198         }
199         if ([pluginsToUnload count] > 0) {
200             LOG(Plugins, "Removed plugins:\n%@", pluginsToUnload);
201         }
202         [reallyNewPlugins release];
203 #endif
204         [pluginsToUnload makeObjectsPerformSelector:@selector(unload)];
205         [plugins minusSet:pluginsToUnload];
206         [plugins unionSet:newPlugins];   
207         [pluginsToUnload release];
208         [newPlugins release];
209     } else {
210         LOG(Plugins, "Plugin database initialization:\n%@", newPlugins);
211         plugins = newPlugins;
212     }
213     
214     // Unregister netscape plug-in WebDocumentViews and WebDocumentRepresentations.
215     NSMutableDictionary *MIMEToViewClass = [WebFrameView _viewTypesAllowImageTypeOmission:NO];
216     NSEnumerator *keyEnumerator = [MIMEToViewClass keyEnumerator];
217     NSString *MIMEType;
218     while ((MIMEType = [keyEnumerator nextObject]) != nil) {
219         Class class = [MIMEToViewClass objectForKey:MIMEType];
220         if (class == [WebNetscapePluginDocumentView class] || class == [WebPluginDocumentView class])
221             [WebView _unregisterViewClassAndRepresentationClassForMIMEType:MIMEType];
222     }
223     
224     // Build a list of MIME types.
225     NSMutableSet *MIMETypes = [[NSMutableSet alloc] init];
226     NSEnumerator *pluginEnumerator = [plugins objectEnumerator];
227     WebBasePluginPackage *plugin;
228     while ((plugin = [pluginEnumerator nextObject]) != nil) {
229         [MIMETypes addObjectsFromArray:[[plugin MIMETypeEnumerator] allObjects]];
230     }
231     
232     // Register plug-in WebDocumentViews and WebDocumentRepresentations.
233     NSEnumerator *MIMEEnumerator = [[MIMETypes allObjects] objectEnumerator];
234     [MIMETypes release];
235     while ((MIMEType = [MIMEEnumerator nextObject]) != nil) {
236         if ([WebView canShowMIMETypeAsHTML:MIMEType])
237             // Don't allow plug-ins to override our core HTML types.
238             continue;
239         plugin = [self pluginForMIMEType:MIMEType];
240         if ([plugin isJavaPlugIn])
241             // Don't register the Java plug-in for a document view since Java files should be downloaded when not embedded.
242             continue;
243         if ([plugin isQuickTimePlugIn] && [[WebFrameView _viewTypesAllowImageTypeOmission:NO] objectForKey:MIMEType])
244             // Don't allow the QT plug-in to override any types because it claims many that we can handle ourselves.
245             continue;
246         if ([plugin isKindOfClass:[WebNetscapePluginPackage class]])
247             [WebView registerViewClass:[WebNetscapePluginDocumentView class] representationClass:[WebNetscapePluginRepresentation class] forMIMEType:MIMEType];
248         else {
249             ASSERT([plugin isKindOfClass:[WebPluginPackage class]]);
250             [WebView registerViewClass:[WebPluginDocumentView class] representationClass:[WebPluginDocumentView class] forMIMEType:MIMEType];
251         }
252         
253     }
254 }
255
256 - (void)dealloc
257 {
258     [plugins release];
259     [super dealloc];
260 }
261
262 @end