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