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