Reviewed by Hyatt.
[WebKit-https.git] / WebKit / Plugins.subproj / WebBasePluginPackage.m
1 //
2 //  WebBasePluginPackage.m
3 //  WebKit
4 //
5 //  Created by Chris Blumenberg on Tue Oct 22 2002.
6 //  Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
7 //
8
9 #import <WebKit/WebBasePluginPackage.h>
10
11 #import <WebKit/WebKitNSStringExtras.h>
12 #import <WebKit/WebNetscapePluginPackage.h>
13 #import <WebKit/WebNSObjectExtras.h>
14 #import <WebKit/WebPluginPackage.h>
15
16 #import <WebKitSystemInterface.h>
17
18 #define JavaCocoaPluginIdentifier       @"com.apple.JavaPluginCocoa"
19 #define JavaCarbonPluginIdentifier      @"com.apple.JavaAppletPlugin"
20 #define JavaCFMPluginFilename           @"Java Applet Plugin Enabler"
21
22 #define QuickTimeCarbonPluginIdentifier       @"com.apple.QuickTime Plugin.plugin"
23 #define QuickTimeCocoaPluginIdentifier        @"com.apple.quicktime.webplugin"
24
25 @interface NSArray (WebPluginExtensions)
26 - (NSArray *)_web_lowercaseStrings;
27 @end;
28
29 @implementation WebBasePluginPackage
30
31 + (WebBasePluginPackage *)pluginWithPath:(NSString *)pluginPath
32 {
33     WebBasePluginPackage *pluginPackage = [[WebPluginPackage alloc] initWithPath:pluginPath];
34
35     if (!pluginPackage) {
36         pluginPackage = [[WebNetscapePluginPackage alloc] initWithPath:pluginPath];
37     }
38
39     return [pluginPackage autorelease];
40 }
41
42 + (NSString *)preferredLocalizationName
43 {
44     return WebCFAutorelease(WKCopyCFLocalizationPreferredName(NULL));
45 }
46
47 - (NSString *)pathByResolvingSymlinksAndAliasesInPath:(NSString *)thePath
48 {
49     NSString *newPath = [thePath stringByResolvingSymlinksInPath];
50
51     FSRef fref;
52     OSStatus err;
53
54     err = FSPathMakeRef((const UInt8 *)[thePath fileSystemRepresentation], &fref, NULL);
55     if (err != noErr) {
56         return newPath;
57     }
58
59     Boolean targetIsFolder;
60     Boolean wasAliased;
61     err = FSResolveAliasFileWithMountFlags(&fref, TRUE, &targetIsFolder, &wasAliased, kResolveAliasFileNoUI);
62     if (err != noErr) {
63         return newPath;
64     }
65
66     if (wasAliased) {
67         CFURLRef URL = CFURLCreateFromFSRef(kCFAllocatorDefault, &fref);
68         newPath = [(NSURL *)URL path];
69         CFRelease(URL);
70     }
71
72     return newPath;
73 }
74
75 - initWithPath:(NSString *)pluginPath
76 {
77     [super init];
78     extensionToMIME = [[NSMutableDictionary alloc] init];
79     path = [[self pathByResolvingSymlinksAndAliasesInPath:pluginPath] retain];
80     bundle = [[NSBundle alloc] initWithPath:path];
81         cfBundle = CFBundleCreate(NULL, (CFURLRef)[NSURL fileURLWithPath:path]);
82     lastModifiedDate = [[[[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:YES] objectForKey:NSFileModificationDate] retain];
83     return self;
84 }
85
86 - (BOOL)getPluginInfoFromBundleAndMIMEDictionary:(NSDictionary *)MIMETypes
87 {
88     if (!bundle) {
89         return NO;
90     }
91     
92     if (!MIMETypes) {
93         MIMETypes = [bundle objectForInfoDictionaryKey:WebPluginMIMETypesKey];
94         if (!MIMETypes) {
95             return NO;
96         }
97     }
98
99     NSMutableDictionary *MIMEToExtensionsDictionary = [NSMutableDictionary dictionary];
100     NSMutableDictionary *MIMEToDescriptionDictionary = [NSMutableDictionary dictionary];
101     NSEnumerator *keyEnumerator = [MIMETypes keyEnumerator];
102     NSDictionary *MIMEDictionary;
103     NSString *MIME, *description;
104     NSArray *extensions;
105
106     while ((MIME = [keyEnumerator nextObject]) != nil) {
107         MIMEDictionary = [MIMETypes objectForKey:MIME];
108         
109         // FIXME: Consider storing disabled MIME types.
110         NSNumber *isEnabled = [MIMEDictionary objectForKey:WebPluginTypeEnabledKey];
111         if (isEnabled && [isEnabled boolValue] == NO) {
112             continue;
113         }
114
115         extensions = [[MIMEDictionary objectForKey:WebPluginExtensionsKey] _web_lowercaseStrings];
116         if ([extensions count] == 0) {
117             extensions = [NSArray arrayWithObject:@""];
118         }
119
120         MIME = [MIME lowercaseString];
121
122         [MIMEToExtensionsDictionary setObject:extensions forKey:MIME];
123
124         description = [MIMEDictionary objectForKey:WebPluginTypeDescriptionKey];
125         if (!description) {
126             description = @"";
127         }
128
129         [MIMEToDescriptionDictionary setObject:description forKey:MIME];
130     }
131
132     [self setMIMEToExtensionsDictionary:MIMEToExtensionsDictionary];
133     [self setMIMEToDescriptionDictionary:MIMEToDescriptionDictionary];
134
135     NSString *filename = [self filename];
136
137     NSString *theName = [bundle objectForInfoDictionaryKey:WebPluginNameKey];
138     if (!theName) {
139         theName = filename;
140     }
141     [self setName:theName];
142
143     description = [bundle objectForInfoDictionaryKey:WebPluginDescriptionKey];
144     if (!description) {
145         description = filename;
146     }
147     [self setPluginDescription:description];
148
149     return YES;
150 }
151
152 - (NSDictionary *)pListForPath:(NSString *)pListPath createFile:(BOOL)createFile
153 {
154     if (createFile && [self load] && BP_CreatePluginMIMETypesPreferences) {
155         BP_CreatePluginMIMETypesPreferences();
156     }
157     
158     NSDictionary *pList = nil;
159     NSData *data = [NSData dataWithContentsOfFile:pListPath];
160     if (data) {
161         pList = [NSPropertyListSerialization propertyListFromData:data
162                                                  mutabilityOption:NSPropertyListImmutable
163                                                            format:nil
164                                                  errorDescription:nil];
165     }
166     
167     return pList;
168 }
169
170 - (BOOL)getPluginInfoFromPLists
171 {
172     if (!bundle) {
173         return NO;
174     }
175     
176     NSDictionary *MIMETypes = nil;
177     NSString *pListFilename = [bundle objectForInfoDictionaryKey:WebPluginMIMETypesFilenameKey];
178     
179     // Check if the MIME types are claimed in a plist in the user's preferences directory.
180     if (pListFilename) {
181         NSString *pListPath = [NSString stringWithFormat:@"%@/Library/Preferences/%@", NSHomeDirectory(), pListFilename];
182         NSDictionary *pList = [self pListForPath:pListPath createFile:NO];
183         if (pList) {
184             // If the plist isn't localized, have the plug-in recreate it in the preferred language.
185             NSString *localizationName = [pList objectForKey:WebPluginLocalizationNameKey];
186             if (![localizationName isEqualToString:[[self class] preferredLocalizationName]]) {
187                 pList = [self pListForPath:pListPath createFile:YES];
188             }
189             MIMETypes = [pList objectForKey:WebPluginMIMETypesKey];
190         } else {
191             // Plist doesn't exist, ask the plug-in to create it.
192             MIMETypes = [[self pListForPath:pListPath createFile:YES] objectForKey:WebPluginMIMETypesKey];
193         }
194     }
195     
196     // Pass the MIME dictionary to the superclass to parse it.
197     return [self getPluginInfoFromBundleAndMIMEDictionary:MIMETypes];
198 }
199
200 - (BOOL)isLoaded
201 {
202     return isLoaded;
203 }
204
205 - (BOOL)load
206 {
207     if (isLoaded && bundle != nil && BP_CreatePluginMIMETypesPreferences == NULL) {
208         BP_CreatePluginMIMETypesPreferences = (BP_CreatePluginMIMETypesPreferencesFuncPtr)CFBundleGetFunctionPointerForName(cfBundle, CFSTR("BP_CreatePluginMIMETypesPreferences"));
209     }
210     return isLoaded;
211 }
212
213 - (void)unload
214 {
215 }
216
217 - (void)dealloc
218 {
219     [self unload];
220     
221     [name release];
222     [path release];
223     [pluginDescription release];
224
225     [MIMEToDescription release];
226     [MIMEToExtensions release];
227     [extensionToMIME release];
228
229     [bundle release];
230         CFRelease(cfBundle);
231
232     [lastModifiedDate release];
233     
234     [super dealloc];
235 }
236
237 - (void)finalize
238 {
239     // FIXME: Bad design to unload at dealloc/finalize time.
240     // Must be fixed for GC.
241     [self unload];
242     [super finalize];
243 }
244
245 - (NSString *)name
246 {
247     return name;
248 }
249
250 - (NSString *)path
251 {
252     return path;
253 }
254
255 - (NSString *)filename
256 {
257     return [path lastPathComponent];
258 }
259
260 - (NSString *)pluginDescription
261 {
262     return pluginDescription;
263 }
264
265 - (NSEnumerator *)extensionEnumerator
266 {
267     return [extensionToMIME keyEnumerator];
268 }
269
270 - (NSEnumerator *)MIMETypeEnumerator
271 {
272     return [MIMEToExtensions keyEnumerator];
273 }
274
275 - (NSString *)descriptionForMIMEType:(NSString *)MIMEType
276 {
277     return [MIMEToDescription objectForKey:MIMEType];
278 }
279
280 - (NSString *)MIMETypeForExtension:(NSString *)extension
281 {
282     return [extensionToMIME objectForKey:extension];
283 }
284
285 - (NSArray *)extensionsForMIMEType:(NSString *)MIMEType
286 {
287     return [MIMEToExtensions objectForKey:MIMEType];
288 }
289
290 - (NSBundle *)bundle
291 {
292     return bundle;
293 }
294
295 - (NSDate *)lastModifiedDate
296 {
297     return lastModifiedDate;
298 }
299
300 - (void)setName:(NSString *)theName
301 {
302     [name release];
303     name = [theName retain];
304 }
305
306 - (void)setPath:(NSString *)thePath
307 {
308     [path release];
309     path = [thePath retain];
310 }
311
312 - (void)setPluginDescription:(NSString *)description
313 {
314     [pluginDescription release];
315     pluginDescription = [description retain];
316 }
317
318 - (void)setMIMEToDescriptionDictionary:(NSDictionary *)MIMEToDescriptionDictionary
319 {
320     [MIMEToDescription release];
321     MIMEToDescription = [MIMEToDescriptionDictionary retain];
322 }
323
324 - (void)setMIMEToExtensionsDictionary:(NSDictionary *)MIMEToExtensionsDictionary
325 {
326     [MIMEToExtensions release];
327     MIMEToExtensions = [MIMEToExtensionsDictionary retain];
328
329     // Reverse the mapping
330     [extensionToMIME removeAllObjects];
331
332     NSEnumerator *MIMEEnumerator = [MIMEToExtensions keyEnumerator], *extensionEnumerator;
333     NSString *MIME, *extension;
334     NSArray *extensions;
335     
336     while ((MIME = [MIMEEnumerator nextObject]) != nil) {
337         extensions = [MIMEToExtensions objectForKey:MIME];
338         extensionEnumerator = [extensions objectEnumerator];
339
340         while ((extension = [extensionEnumerator nextObject]) != nil) {
341             if (![extension isEqualToString:@""]) {
342                 [extensionToMIME setObject:MIME forKey:extension];
343             }
344         }
345     }
346 }
347
348 - (NSString *)description
349 {
350     return [NSString stringWithFormat:@"name: %@\npath: %@\nmimeTypes:\n%@\npluginDescription:%@",
351         name, path, [MIMEToExtensions description], [MIMEToDescription description], pluginDescription];
352 }
353
354 - (BOOL)isEqual:(id)object
355 {
356     return ([object isKindOfClass:[WebBasePluginPackage class]] &&
357             [[object name] isEqualToString:name] &&
358             [[object lastModifiedDate] isEqual:lastModifiedDate]);
359 }
360
361 - (unsigned)hash
362 {
363     return [[name stringByAppendingString:[lastModifiedDate description]] hash];
364 }
365
366 - (BOOL)isQuickTimePlugIn
367 {
368     NSString *bundleIdentifier = [[self bundle] bundleIdentifier];
369     return [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:QuickTimeCarbonPluginIdentifier] || 
370         [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:QuickTimeCocoaPluginIdentifier];
371 }
372
373 - (BOOL)isJavaPlugIn
374 {
375     NSString *bundleIdentifier = [[self bundle] bundleIdentifier];
376     return [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:JavaCocoaPluginIdentifier] || 
377         [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:JavaCarbonPluginIdentifier] ||
378         [[path lastPathComponent] _webkit_isCaseInsensitiveEqualToString:JavaCFMPluginFilename];
379 }
380
381 @end
382
383 @implementation NSArray (WebPluginExtensions)
384
385 - (NSArray *)_web_lowercaseStrings
386 {
387     NSMutableArray *lowercaseStrings = [NSMutableArray arrayWithCapacity:[self count]];
388     NSEnumerator *strings = [self objectEnumerator];
389     NSString *string;
390
391     while ((string = [strings nextObject]) != nil) {
392         if ([string isKindOfClass:[NSString class]]) {
393             [lowercaseStrings addObject:[string lowercaseString]];
394         }
395     }
396
397     return lowercaseStrings;
398 }
399
400 @end;