128834e790db12bb76244d2da1e8fbb1f50e8cf4
[WebKit-https.git] / Source / WebKit / mac / Plugins / WebBasePluginPackage.mm
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/WebBasePluginPackage.h>
30
31 #import <algorithm>
32 #import <WebCore/WebCoreObjCExtras.h>
33 #import <WebKit/WebKitNSStringExtras.h>
34 #import <WebKit/WebNSObjectExtras.h>
35 #import <WebKit/WebNetscapePluginPackage.h>
36 #import <WebKit/WebPluginPackage.h>
37 #import <runtime/InitializeThreading.h>
38 #import <wtf/Assertions.h>
39 #import <wtf/Threading.h>
40 #import <wtf/Vector.h>
41 #import <wtf/text/CString.h>
42
43 #import <WebKitSystemInterface.h>
44
45 #import "WebKitLogging.h"
46 #import "WebTypesInternal.h"
47
48 #import <mach-o/arch.h>
49 #import <mach-o/fat.h>
50 #import <mach-o/loader.h>
51
52 #define JavaCocoaPluginIdentifier   "com.apple.JavaPluginCocoa"
53 #define JavaCarbonPluginIdentifier  "com.apple.JavaAppletPlugin"
54 #define JavaCFMPluginFilename       "Java Applet Plugin Enabler"
55
56 #define QuickTimeCarbonPluginIdentifier       "com.apple.QuickTime Plugin.plugin"
57 #define QuickTimeCocoaPluginIdentifier        "com.apple.quicktime.webplugin"
58
59 @interface NSArray (WebPluginExtensions)
60 - (NSArray *)_web_lowercaseStrings;
61 @end;
62
63 using namespace std;
64 using namespace WebCore;
65
66 @implementation WebBasePluginPackage
67
68 + (void)initialize
69 {
70     JSC::initializeThreading();
71     WTF::initializeMainThreadToProcessMainThread();
72 #ifndef BUILDING_ON_TIGER
73     WebCoreObjCFinalizeOnMainThread(self);
74 #endif
75 }
76
77 + (WebBasePluginPackage *)pluginWithPath:(NSString *)pluginPath
78 {
79     
80     WebBasePluginPackage *pluginPackage = [[WebPluginPackage alloc] initWithPath:pluginPath];
81
82     if (!pluginPackage) {
83 #if ENABLE(NETSCAPE_PLUGIN_API)
84         pluginPackage = [[WebNetscapePluginPackage alloc] initWithPath:pluginPath];
85 #else
86         return nil;
87 #endif
88     }
89
90     return [pluginPackage autorelease];
91 }
92
93 + (NSString *)preferredLocalizationName
94 {
95     return WebCFAutorelease(WKCopyCFLocalizationPreferredName(NULL));
96 }
97
98 static NSString *pathByResolvingSymlinksAndAliases(NSString *thePath)
99 {
100     NSString *newPath = [thePath stringByResolvingSymlinksInPath];
101
102     FSRef fref;
103     OSStatus err;
104
105     err = FSPathMakeRef((const UInt8 *)[thePath fileSystemRepresentation], &fref, NULL);
106     if (err != noErr)
107         return newPath;
108
109     Boolean targetIsFolder;
110     Boolean wasAliased;
111     err = FSResolveAliasFileWithMountFlags(&fref, TRUE, &targetIsFolder, &wasAliased, kResolveAliasFileNoUI);
112     if (err != noErr)
113         return newPath;
114
115     if (wasAliased) {
116         CFURLRef URL = CFURLCreateFromFSRef(kCFAllocatorDefault, &fref);
117         newPath = [(NSURL *)URL path];
118         CFRelease(URL);
119     }
120
121     return newPath;
122 }
123
124 - (id)initWithPath:(NSString *)pluginPath
125 {
126     if (!(self = [super init]))
127         return nil;
128         
129     path = pathByResolvingSymlinksAndAliases(pluginPath);
130     cfBundle.adoptCF(CFBundleCreate(kCFAllocatorDefault, (CFURLRef)[NSURL fileURLWithPath:path]));
131
132 #ifndef __ppc__
133     // 32-bit PowerPC is the only platform where non-bundled CFM plugins are supported
134     if (!cfBundle) {
135         [self release];
136         return nil;
137     }
138 #endif
139     
140     return self;
141 }
142
143 - (void)unload
144 {
145 }
146
147 - (void)createPropertyListFile
148 {
149     if ([self load] && BP_CreatePluginMIMETypesPreferences) {
150         BP_CreatePluginMIMETypesPreferences();
151         [self unload];
152     }
153 }
154
155 - (NSDictionary *)pListForPath:(NSString *)pListPath createFile:(BOOL)createFile
156 {
157     if (createFile)
158         [self createPropertyListFile];
159     
160     NSDictionary *pList = nil;
161     NSData *data = [NSData dataWithContentsOfFile:pListPath];
162     if (data) {
163         pList = [NSPropertyListSerialization propertyListFromData:data
164                                                  mutabilityOption:NSPropertyListImmutable
165                                                            format:nil
166                                                  errorDescription:nil];
167     }
168     
169     return pList;
170 }
171
172 - (id)_objectForInfoDictionaryKey:(NSString *)key
173 {
174     CFDictionaryRef bundleInfoDictionary = CFBundleGetInfoDictionary(cfBundle.get());
175     if (!bundleInfoDictionary)
176         return nil;
177
178     return (id)CFDictionaryGetValue(bundleInfoDictionary, key);
179 }
180
181 - (BOOL)getPluginInfoFromPLists
182 {
183     if (!cfBundle)
184         return NO;
185     
186     NSDictionary *MIMETypes = nil;
187     NSString *pListFilename = [self _objectForInfoDictionaryKey:WebPluginMIMETypesFilenameKey];
188     
189     // Check if the MIME types are claimed in a plist in the user's preferences directory.
190     if (pListFilename) {
191         NSString *pListPath = [NSString stringWithFormat:@"%@/Library/Preferences/%@", NSHomeDirectory(), pListFilename];
192         NSDictionary *pList = [self pListForPath:pListPath createFile:NO];
193         if (pList) {
194             // If the plist isn't localized, have the plug-in recreate it in the preferred language.
195             NSString *localizationName = [pList objectForKey:WebPluginLocalizationNameKey];
196             if (![localizationName isEqualToString:[[self class] preferredLocalizationName]])
197                 pList = [self pListForPath:pListPath createFile:YES];
198             MIMETypes = [pList objectForKey:WebPluginMIMETypesKey];
199         } else
200             // Plist doesn't exist, ask the plug-in to create it.
201             MIMETypes = [[self pListForPath:pListPath createFile:YES] objectForKey:WebPluginMIMETypesKey];
202     }
203
204     if (!MIMETypes) {
205         MIMETypes = [self _objectForInfoDictionaryKey:WebPluginMIMETypesKey];
206         if (!MIMETypes)
207             return NO;
208     }
209
210     NSEnumerator *keyEnumerator = [MIMETypes keyEnumerator];
211     NSDictionary *MIMEDictionary;
212     NSString *MIME, *description;
213     NSArray *extensions;
214
215     while ((MIME = [keyEnumerator nextObject]) != nil) {
216         MIMEDictionary = [MIMETypes objectForKey:MIME];
217         
218         // FIXME: Consider storing disabled MIME types.
219         NSNumber *isEnabled = [MIMEDictionary objectForKey:WebPluginTypeEnabledKey];
220         if (isEnabled && [isEnabled boolValue] == NO)
221             continue;
222
223         MimeClassInfo mimeClassInfo;
224         
225         extensions = [[MIMEDictionary objectForKey:WebPluginExtensionsKey] _web_lowercaseStrings];
226         for (NSUInteger i = 0; i < [extensions count]; ++i) {
227             // The DivX plug-in lists multiple extensions in a comma separated string instead of using
228             // multiple array elements in the property list. Work around this here by splitting the
229             // extension string into components.
230             NSArray *extensionComponents = [[extensions objectAtIndex:i] componentsSeparatedByString:@","];
231
232             for (NSString *extension in extensionComponents)
233                 mimeClassInfo.extensions.append(extension);
234         }
235
236         if ([extensions count] == 0)
237             extensions = [NSArray arrayWithObject:@""];
238
239         mimeClassInfo.type = String(MIME).lower();
240
241         description = [MIMEDictionary objectForKey:WebPluginTypeDescriptionKey];
242         mimeClassInfo.desc = description;
243
244         pluginInfo.mimes.append(mimeClassInfo);
245         if (!description)
246             description = @"";
247     }
248
249     NSString *filename = [(NSString *)path lastPathComponent];
250     pluginInfo.file = filename;
251
252     NSString *theName = [self _objectForInfoDictionaryKey:WebPluginNameKey];
253     if (!theName)
254         theName = filename;
255     pluginInfo.name = theName;
256
257     description = [self _objectForInfoDictionaryKey:WebPluginDescriptionKey];
258     if (!description)
259         description = filename;
260     pluginInfo.desc = description;
261
262     return YES;
263 }
264
265 - (BOOL)load
266 {
267     if (cfBundle && !BP_CreatePluginMIMETypesPreferences)
268         BP_CreatePluginMIMETypesPreferences = (BP_CreatePluginMIMETypesPreferencesFuncPtr)CFBundleGetFunctionPointerForName(cfBundle.get(), CFSTR("BP_CreatePluginMIMETypesPreferences"));
269     
270     return YES;
271 }
272
273 - (void)dealloc
274 {
275     ASSERT(!pluginDatabases || [pluginDatabases count] == 0);
276     [pluginDatabases release];
277     
278     [super dealloc];
279 }
280
281 - (void)finalize
282 {
283     ASSERT_MAIN_THREAD();
284     ASSERT(!pluginDatabases || [pluginDatabases count] == 0);
285     [pluginDatabases release];
286
287     [super finalize];
288 }
289
290 - (const String&)path
291 {
292     return path;
293 }
294
295 - (const PluginInfo&)pluginInfo
296 {
297     return pluginInfo;
298 }
299
300 - (BOOL)supportsExtension:(const String&)extension
301 {
302     ASSERT(extension.lower() == extension);
303     
304     for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) {
305         const Vector<String>& extensions = pluginInfo.mimes[i].extensions;
306
307         if (find(extensions.begin(), extensions.end(), extension) != extensions.end())
308             return YES;
309     }
310
311     return NO;
312 }
313
314 - (BOOL)supportsMIMEType:(const WTF::String&)mimeType
315 {
316     ASSERT(mimeType.lower() == mimeType);
317     
318     for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) {
319         if (pluginInfo.mimes[i].type == mimeType)
320             return YES;
321     }
322     
323     return NO;
324 }
325
326 - (NSString *)MIMETypeForExtension:(const String&)extension
327 {
328     ASSERT(extension.lower() == extension);
329     
330     for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) {
331         const MimeClassInfo& mimeClassInfo = pluginInfo.mimes[i];
332         const Vector<String>& extensions = mimeClassInfo.extensions;
333
334         if (find(extensions.begin(), extensions.end(), extension) != extensions.end())
335             return mimeClassInfo.type;
336     }
337
338     return nil;
339 }
340
341 - (BOOL)isQuickTimePlugIn
342 {
343     const String& bundleIdentifier = [self bundleIdentifier];
344     return bundleIdentifier == QuickTimeCocoaPluginIdentifier || bundleIdentifier == QuickTimeCocoaPluginIdentifier;
345 }
346
347 - (BOOL)isJavaPlugIn
348 {
349     const String& bundleIdentifier = [self bundleIdentifier];
350     return bundleIdentifier == JavaCocoaPluginIdentifier || bundleIdentifier == JavaCarbonPluginIdentifier ||
351         equalIgnoringCase(pluginInfo.file, JavaCFMPluginFilename);
352 }
353
354 static inline void swapIntsInHeader(uint32_t* rawData, size_t length)
355 {
356     for (size_t i = 0; i < length; ++i) 
357         rawData[i] = OSSwapInt32(rawData[i]);
358 }
359
360 - (BOOL)isNativeLibraryData:(NSData *)data
361 {
362     NSUInteger sizeInBytes = [data length];
363     Vector<uint32_t, 128> rawData((sizeInBytes + 3) / 4);
364     memcpy(rawData.data(), [data bytes], sizeInBytes);
365     
366     unsigned numArchs = 0;
367     struct fat_arch singleArch = { 0, 0, 0, 0, 0 };
368     struct fat_arch* archs = 0;
369        
370     if (sizeInBytes >= sizeof(struct mach_header_64)) {
371         uint32_t magic = *rawData.data();
372         
373         if (magic == MH_MAGIC || magic == MH_CIGAM) {
374             // We have a 32-bit thin binary
375             struct mach_header* header = (struct mach_header*)rawData.data();
376
377             // Check if we need to swap the bytes
378             if (magic == MH_CIGAM)
379                 swapIntsInHeader(rawData.data(), rawData.size());
380     
381             singleArch.cputype = header->cputype;
382             singleArch.cpusubtype = header->cpusubtype;
383
384             archs = &singleArch;
385             numArchs = 1;
386         } else if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64) {
387             // We have a 64-bit thin binary
388             struct mach_header_64* header = (struct mach_header_64*)rawData.data();
389
390             // Check if we need to swap the bytes
391             if (magic == MH_CIGAM_64)
392                 swapIntsInHeader(rawData.data(), rawData.size());
393             
394             singleArch.cputype = header->cputype;
395             singleArch.cpusubtype = header->cpusubtype;
396             
397             archs = &singleArch;
398             numArchs = 1;
399         } else if (magic == FAT_MAGIC || magic == FAT_CIGAM) {
400             // We have a fat (universal) binary
401
402             // Check if we need to swap the bytes
403             if (magic == FAT_CIGAM)
404                 swapIntsInHeader(rawData.data(), rawData.size());
405             
406             COMPILE_ASSERT(sizeof(struct fat_header) % sizeof(uint32_t) == 0, struct_fat_header_must_be_integral_size_of_uint32_t);
407             archs = reinterpret_cast<struct fat_arch*>(rawData.data() + sizeof(struct fat_header) / sizeof(uint32_t));
408             numArchs = reinterpret_cast<struct fat_header*>(rawData.data())->nfat_arch;
409             
410             unsigned maxArchs = (sizeInBytes - sizeof(struct fat_header)) / sizeof(struct fat_arch);
411             if (numArchs > maxArchs)
412                 numArchs = maxArchs;
413         }            
414     }
415     
416     if (!archs || !numArchs)
417         return NO;
418     
419     const NXArchInfo* localArch = NXGetLocalArchInfo();
420     if (!localArch)
421         return NO;
422     
423     cpu_type_t cputype = localArch->cputype;
424     cpu_subtype_t cpusubtype = localArch->cpusubtype;
425     
426 #ifdef __x86_64__
427     // NXGetLocalArchInfo returns CPU_TYPE_X86 even when running in 64-bit. 
428     // See <rdar://problem/4996965> for more information.
429     cputype = CPU_TYPE_X86_64;
430 #endif
431     
432     return NXFindBestFatArch(cputype, cpusubtype, archs, numArchs) != 0;
433 }
434
435 - (UInt32)versionNumber
436 {
437     // CFBundleGetVersionNumber doesn't work with all possible versioning schemes, but we think for now it's good enough for us.
438     return CFBundleGetVersionNumber(cfBundle.get());
439 }
440
441 - (void)wasAddedToPluginDatabase:(WebPluginDatabase *)database
442 {    
443     if (!pluginDatabases)
444         pluginDatabases = [[NSMutableSet alloc] init];
445         
446     ASSERT(![pluginDatabases containsObject:database]);
447     [pluginDatabases addObject:database];
448 }
449
450 - (void)wasRemovedFromPluginDatabase:(WebPluginDatabase *)database
451 {
452     ASSERT(pluginDatabases);
453     ASSERT([pluginDatabases containsObject:database]);
454
455     [pluginDatabases removeObject:database];
456 }
457
458 - (WTF::String)bundleIdentifier
459 {
460     return CFBundleGetIdentifier(cfBundle.get());
461 }
462
463 @end
464
465 @implementation NSArray (WebPluginExtensions)
466
467 - (NSArray *)_web_lowercaseStrings
468 {
469     NSMutableArray *lowercaseStrings = [NSMutableArray arrayWithCapacity:[self count]];
470     NSEnumerator *strings = [self objectEnumerator];
471     NSString *string;
472
473     while ((string = [strings nextObject]) != nil) {
474         if ([string isKindOfClass:[NSString class]])
475             [lowercaseStrings addObject:[string lowercaseString]];
476     }
477
478     return lowercaseStrings;
479 }
480
481 @end