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