|
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/WebHTMLRepresentation.h> |
|
37 #import <WebKit/WebHTMLView.h> |
|
38 #import <WebKit/WebKitLogging.h> |
|
39 #import <WebKit/WebNetscapePluginPackage.h> |
|
40 #import <WebKit/WebPluginPackage.h> |
|
41 #import <WebKit/WebViewPrivate.h> |
|
42 #import <WebKitSystemInterface.h> |
|
43 |
|
44 @interface WebPluginDatabase (Internal) |
|
45 + (NSArray *)_defaultPlugInPaths; |
|
46 - (NSArray *)_plugInPaths; |
|
47 - (void)_addPlugin:(WebBasePluginPackage *)plugin; |
|
48 - (void)_removePlugin:(WebBasePluginPackage *)plugin; |
|
49 - (NSMutableSet *)_scanForNewPlugins; |
|
50 @end |
|
51 |
|
52 @implementation WebPluginDatabase |
|
53 |
|
54 static WebPluginDatabase *sharedDatabase = nil; |
|
55 |
|
56 + (WebPluginDatabase *)sharedDatabase |
|
57 { |
|
58 if (!sharedDatabase) { |
|
59 sharedDatabase = [[WebPluginDatabase alloc] init]; |
|
60 [sharedDatabase setPlugInPaths:[self _defaultPlugInPaths]]; |
|
61 [sharedDatabase refresh]; |
|
62 } |
|
63 |
|
64 return sharedDatabase; |
|
65 } |
|
66 |
|
67 + (void)closeSharedDatabase |
|
68 { |
|
69 [sharedDatabase close]; |
|
70 } |
|
71 |
|
72 - (WebBasePluginPackage *)pluginForKey:(NSString *)key withEnumeratorSelector:(SEL)enumeratorSelector |
|
73 { |
|
74 WebBasePluginPackage *plugin = nil; |
|
75 WebBasePluginPackage *webPlugin = nil; |
|
76 #ifndef __LP64__ |
|
77 WebBasePluginPackage *CFMPlugin = nil; |
|
78 WebBasePluginPackage *machoPlugin = nil; |
|
79 #endif |
|
80 NSEnumerator *pluginEnumerator = [plugins objectEnumerator]; |
|
81 key = [key lowercaseString]; |
|
82 |
|
83 while ((plugin = [pluginEnumerator nextObject]) != nil) { |
|
84 if ([[[plugin performSelector:enumeratorSelector] allObjects] containsObject:key]) { |
|
85 if ([plugin isKindOfClass:[WebPluginPackage class]]) { |
|
86 if (!webPlugin) |
|
87 webPlugin = plugin; |
|
88 } |
|
89 #ifndef __LP64__ |
|
90 else if([plugin isKindOfClass:[WebNetscapePluginPackage class]]) { |
|
91 WebExecutableType executableType = [(WebNetscapePluginPackage *)plugin executableType]; |
|
92 if (executableType == WebCFMExecutableType) { |
|
93 if (!CFMPlugin) |
|
94 CFMPlugin = plugin; |
|
95 } else if (executableType == WebMachOExecutableType) { |
|
96 if (!machoPlugin) |
|
97 machoPlugin = plugin; |
|
98 } else { |
|
99 ASSERT_NOT_REACHED(); |
|
100 } |
|
101 } else { |
|
102 ASSERT_NOT_REACHED(); |
|
103 } |
|
104 #endif |
|
105 } |
|
106 } |
|
107 |
|
108 // Allow other plug-ins to win over QT because if the user has installed a plug-in that can handle a type |
|
109 // that the QT plug-in can handle, they probably intended to override QT. |
|
110 if (webPlugin && ![webPlugin isQuickTimePlugIn]) |
|
111 return webPlugin; |
|
112 |
|
113 #ifndef __LP64__ |
|
114 else if (machoPlugin && ![machoPlugin isQuickTimePlugIn]) |
|
115 return machoPlugin; |
|
116 else if (CFMPlugin && ![CFMPlugin isQuickTimePlugIn]) |
|
117 return CFMPlugin; |
|
118 #endif |
|
119 else if (webPlugin) |
|
120 return webPlugin; |
|
121 #ifndef __LP64__ |
|
122 else if (machoPlugin) |
|
123 return machoPlugin; |
|
124 else if (CFMPlugin) |
|
125 return CFMPlugin; |
|
126 #endif |
|
127 return nil; |
|
128 } |
|
129 |
|
130 - (WebBasePluginPackage *)pluginForMIMEType:(NSString *)MIMEType |
|
131 { |
|
132 return [self pluginForKey:[MIMEType lowercaseString] |
|
133 withEnumeratorSelector:@selector(MIMETypeEnumerator)]; |
|
134 } |
|
135 |
|
136 - (WebBasePluginPackage *)pluginForExtension:(NSString *)extension |
|
137 { |
|
138 WebBasePluginPackage *plugin = [self pluginForKey:[extension lowercaseString] |
|
139 withEnumeratorSelector:@selector(extensionEnumerator)]; |
|
140 if (!plugin) { |
|
141 // If no plug-in was found from the extension, attempt to map from the extension to a MIME type |
|
142 // and find the a plug-in from the MIME type. This is done in case the plug-in has not fully specified |
|
143 // an extension <-> MIME type mapping. |
|
144 NSString *MIMEType = WKGetMIMETypeForExtension(extension); |
|
145 if ([MIMEType length] > 0) |
|
146 plugin = [self pluginForMIMEType:MIMEType]; |
|
147 } |
|
148 return plugin; |
|
149 } |
|
150 |
|
151 - (NSArray *)plugins |
|
152 { |
|
153 return [plugins allValues]; |
|
154 } |
|
155 |
|
156 static NSArray *additionalWebPlugInPaths; |
|
157 |
|
158 + (void)setAdditionalWebPlugInPaths:(NSArray *)additionalPaths |
|
159 { |
|
160 if (additionalPaths == additionalWebPlugInPaths) |
|
161 return; |
|
162 |
|
163 [additionalWebPlugInPaths release]; |
|
164 additionalWebPlugInPaths = [additionalPaths copy]; |
|
165 |
|
166 // One might be tempted to add additionalWebPlugInPaths to the global WebPluginDatabase here. |
|
167 // For backward compatibility with earlier versions of the +setAdditionalWebPlugInPaths: SPI, |
|
168 // we need to save a copy of the additional paths and not cause a refresh of the plugin DB |
|
169 // at this time. |
|
170 // See Radars 4608487 and 4609047. |
|
171 } |
|
172 |
|
173 - (void)setPlugInPaths:(NSArray *)newPaths |
|
174 { |
|
175 if (plugInPaths == newPaths) |
|
176 return; |
|
177 |
|
178 [plugInPaths release]; |
|
179 plugInPaths = [newPaths copy]; |
|
180 } |
|
181 |
|
182 - (void)close |
|
183 { |
|
184 NSEnumerator *pluginEnumerator = [[self plugins] objectEnumerator]; |
|
185 WebBasePluginPackage *plugin; |
|
186 while ((plugin = [pluginEnumerator nextObject]) != nil) |
|
187 [self _removePlugin:plugin]; |
|
188 [plugins release]; |
|
189 plugins = nil; |
|
190 } |
|
191 |
|
192 - (id)init |
|
193 { |
|
194 if (!(self = [super init])) |
|
195 return nil; |
|
196 |
|
197 registeredMIMETypes = [[NSMutableSet alloc] init]; |
|
198 |
|
199 return self; |
|
200 } |
|
201 |
|
202 - (void)dealloc |
|
203 { |
|
204 [plugInPaths release]; |
|
205 [plugins release]; |
|
206 [registeredMIMETypes release]; |
|
207 |
|
208 [super dealloc]; |
|
209 } |
|
210 |
|
211 - (void)refresh |
|
212 { |
|
213 // This method does a bit of autoreleasing, so create an autorelease pool to ensure that calling |
|
214 // -refresh multiple times does not bloat the default pool. |
|
215 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
|
216 |
|
217 // Create map from plug-in path to WebBasePluginPackage |
|
218 if (!plugins) |
|
219 plugins = [[NSMutableDictionary alloc] initWithCapacity:12]; |
|
220 |
|
221 // Find all plug-ins on disk |
|
222 NSMutableSet *newPlugins = [self _scanForNewPlugins]; |
|
223 |
|
224 // Find plug-ins to remove from database (i.e., plug-ins that no longer exist on disk) |
|
225 NSMutableSet *pluginsToRemove = [NSMutableSet set]; |
|
226 NSEnumerator *pluginEnumerator = [plugins objectEnumerator]; |
|
227 WebBasePluginPackage *plugin; |
|
228 while ((plugin = [pluginEnumerator nextObject]) != nil) { |
|
229 // Any plug-ins that were removed from disk since the last refresh should be removed from |
|
230 // the database. |
|
231 if (![newPlugins containsObject:plugin]) |
|
232 [pluginsToRemove addObject:plugin]; |
|
233 |
|
234 // Remove every member of 'plugins' from 'newPlugins'. After this loop exits, 'newPlugins' |
|
235 // will be the set of new plug-ins that should be added to the database. |
|
236 [newPlugins removeObject:plugin]; |
|
237 } |
|
238 |
|
239 #if !LOG_DISABLED |
|
240 if ([newPlugins count] > 0) |
|
241 LOG(Plugins, "New plugins:\n%@", newPlugins); |
|
242 if ([pluginsToRemove count] > 0) |
|
243 LOG(Plugins, "Removed plugins:\n%@", pluginsToRemove); |
|
244 #endif |
|
245 |
|
246 // Remove plugins from database |
|
247 pluginEnumerator = [pluginsToRemove objectEnumerator]; |
|
248 while ((plugin = [pluginEnumerator nextObject]) != nil) |
|
249 [self _removePlugin:plugin]; |
|
250 |
|
251 // Add new plugins to database |
|
252 pluginEnumerator = [newPlugins objectEnumerator]; |
|
253 while ((plugin = [pluginEnumerator nextObject]) != nil) |
|
254 [self _addPlugin:plugin]; |
|
255 |
|
256 // Build a list of MIME types. |
|
257 NSMutableSet *MIMETypes = [[NSMutableSet alloc] init]; |
|
258 pluginEnumerator = [plugins objectEnumerator]; |
|
259 while ((plugin = [pluginEnumerator nextObject]) != nil) |
|
260 [MIMETypes addObjectsFromArray:[[plugin MIMETypeEnumerator] allObjects]]; |
|
261 |
|
262 // Register plug-in views and representations. |
|
263 NSEnumerator *MIMEEnumerator = [MIMETypes objectEnumerator]; |
|
264 NSString *MIMEType; |
|
265 while ((MIMEType = [MIMEEnumerator nextObject]) != nil) { |
|
266 [registeredMIMETypes addObject:MIMEType]; |
|
267 |
|
268 if ([WebView canShowMIMETypeAsHTML:MIMEType]) |
|
269 // Don't allow plug-ins to override our core HTML types. |
|
270 continue; |
|
271 plugin = [self pluginForMIMEType:MIMEType]; |
|
272 if ([plugin isJavaPlugIn]) |
|
273 // Don't register the Java plug-in for a document view since Java files should be downloaded when not embedded. |
|
274 continue; |
|
275 if ([plugin isQuickTimePlugIn] && [[WebFrameView _viewTypesAllowImageTypeOmission:NO] objectForKey:MIMEType]) |
|
276 // Don't allow the QT plug-in to override any types because it claims many that we can handle ourselves. |
|
277 continue; |
|
278 |
|
279 if (self == sharedDatabase) |
|
280 [WebView registerViewClass:[WebHTMLView class] representationClass:[WebHTMLRepresentation class] forMIMEType:MIMEType]; |
|
281 } |
|
282 [MIMETypes release]; |
|
283 |
|
284 [pool drain]; |
|
285 } |
|
286 |
|
287 - (BOOL)isMIMETypeRegistered:(NSString *)MIMEType |
|
288 { |
|
289 return [registeredMIMETypes containsObject:MIMEType]; |
|
290 } |
|
291 |
|
292 @end |
|
293 |
|
294 @implementation WebPluginDatabase (Internal) |
|
295 |
|
296 + (NSArray *)_defaultPlugInPaths |
|
297 { |
|
298 // Plug-ins are found in order of precedence. |
|
299 // If there are duplicates, the first found plug-in is used. |
|
300 // For example, if there is a QuickTime.plugin in the users's home directory |
|
301 // that is used instead of the /Library/Internet Plug-ins version. |
|
302 // The purpose is to allow non-admin users to update their plug-ins. |
|
303 return [NSArray arrayWithObjects: |
|
304 [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Internet Plug-Ins"], |
|
305 @"/Library/Internet Plug-Ins", |
|
306 [[NSBundle mainBundle] builtInPlugInsPath], |
|
307 nil]; |
|
308 } |
|
309 |
|
310 - (NSArray *)_plugInPaths |
|
311 { |
|
312 if (self == sharedDatabase && additionalWebPlugInPaths) { |
|
313 // Add additionalWebPlugInPaths to the global WebPluginDatabase. We do this here for |
|
314 // backward compatibility with earlier versions of the +setAdditionalWebPlugInPaths: SPI, |
|
315 // which simply saved a copy of the additional paths and did not cause the plugin DB to |
|
316 // refresh. See Radars 4608487 and 4609047. |
|
317 NSMutableArray *modifiedPlugInPaths = [[plugInPaths mutableCopy] autorelease]; |
|
318 [modifiedPlugInPaths addObjectsFromArray:additionalWebPlugInPaths]; |
|
319 return modifiedPlugInPaths; |
|
320 } else |
|
321 return plugInPaths; |
|
322 } |
|
323 |
|
324 - (void)_addPlugin:(WebBasePluginPackage *)plugin |
|
325 { |
|
326 ASSERT(plugin); |
|
327 NSString *pluginPath = [plugin path]; |
|
328 ASSERT(pluginPath); |
|
329 [plugins setObject:plugin forKey:pluginPath]; |
|
330 [plugin wasAddedToPluginDatabase:self]; |
|
331 } |
|
332 |
|
333 - (void)_removePlugin:(WebBasePluginPackage *)plugin |
|
334 { |
|
335 ASSERT(plugin); |
|
336 |
|
337 // Unregister plug-in's MIME type registrations |
|
338 NSEnumerator *MIMETypeEnumerator = [plugin MIMETypeEnumerator]; |
|
339 NSString *MIMEType; |
|
340 while ((MIMEType = [MIMETypeEnumerator nextObject])) { |
|
341 if ([registeredMIMETypes containsObject:MIMEType]) { |
|
342 if (self == sharedDatabase) |
|
343 [WebView _unregisterViewClassAndRepresentationClassForMIMEType:MIMEType]; |
|
344 [registeredMIMETypes removeObject:MIMEType]; |
|
345 } |
|
346 } |
|
347 |
|
348 // Remove plug-in from database |
|
349 NSString *pluginPath = [plugin path]; |
|
350 ASSERT(pluginPath); |
|
351 [plugin retain]; |
|
352 [plugins removeObjectForKey:pluginPath]; |
|
353 [plugin wasRemovedFromPluginDatabase:self]; |
|
354 [plugin release]; |
|
355 } |
|
356 |
|
357 - (NSMutableSet *)_scanForNewPlugins |
|
358 { |
|
359 NSMutableSet *newPlugins = [NSMutableSet set]; |
|
360 NSEnumerator *directoryEnumerator = [[self _plugInPaths] objectEnumerator]; |
|
361 NSMutableSet *uniqueFilenames = [[NSMutableSet alloc] init]; |
|
362 NSFileManager *fileManager = [NSFileManager defaultManager]; |
|
363 NSString *pluginDirectory; |
|
364 while ((pluginDirectory = [directoryEnumerator nextObject]) != nil) { |
|
365 // Get contents of each plug-in directory |
|
366 NSEnumerator *filenameEnumerator = [[fileManager directoryContentsAtPath:pluginDirectory] objectEnumerator]; |
|
367 NSString *filename; |
|
368 while ((filename = [filenameEnumerator nextObject]) != nil) { |
|
369 // Unique plug-ins by filename |
|
370 if ([uniqueFilenames containsObject:filename]) |
|
371 continue; |
|
372 [uniqueFilenames addObject:filename]; |
|
373 |
|
374 // Create a plug-in package for this path |
|
375 NSString *pluginPath = [pluginDirectory stringByAppendingPathComponent:filename]; |
|
376 WebBasePluginPackage *pluginPackage = [plugins objectForKey:pluginPath]; |
|
377 if (!pluginPackage) |
|
378 pluginPackage = [WebBasePluginPackage pluginWithPath:pluginPath]; |
|
379 if (pluginPackage) |
|
380 [newPlugins addObject:pluginPackage]; |
|
381 } |
|
382 } |
|
383 [uniqueFilenames release]; |
|
384 |
|
385 return newPlugins; |
|
386 } |
|
387 |
|
388 @end |