Home | History | Annotate | Download | only in Plugins
      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
    482