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/RunLoop.h> 33#import <WebCore/WebCoreObjCExtras.h> 34#import <WebKit/WebKitNSStringExtras.h> 35#import <WebKit/WebNetscapePluginPackage.h> 36#import <WebKit/WebPluginPackage.h> 37#import <runtime/InitializeThreading.h> 38#import <wtf/Assertions.h> 39#import <wtf/MainThread.h> 40#import <wtf/ObjcRuntimeExtras.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 63using namespace WebCore; 64 65@implementation WebBasePluginPackage 66 67+ (void)initialize 68{ 69 JSC::initializeThreading(); 70 WTF::initializeMainThreadToProcessMainThread(); 71 WebCore::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 HardAutorelease(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:…]. 101static 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 propertyListFromData:data 167 mutabilityOption:NSPropertyListImmutable 168 format:nil 169 errorDescription:nil]; 170 } 171 172 return pList; 173} 174 175- (id)_objectForInfoDictionaryKey:(NSString *)key 176{ 177 CFDictionaryRef bundleInfoDictionary = CFBundleGetInfoDictionary(cfBundle.get()); 178 if (!bundleInfoDictionary) 179 return nil; 180 181 return (id)CFDictionaryGetValue(bundleInfoDictionary, key); 182} 183 184- (BOOL)getPluginInfoFromPLists 185{ 186 if (!cfBundle) 187 return NO; 188 189 NSDictionary *MIMETypes = nil; 190 NSString *pListFilename = [self _objectForInfoDictionaryKey:WebPluginMIMETypesFilenameKey]; 191 192 // Check if the MIME types are claimed in a plist in the user's preferences directory. 193 if (pListFilename) { 194 NSString *pListPath = [NSString stringWithFormat:@"%@/Library/Preferences/%@", NSHomeDirectory(), pListFilename]; 195 NSDictionary *pList = [self pListForPath:pListPath createFile:NO]; 196 if (pList) { 197 // If the plist isn't localized, have the plug-in recreate it in the preferred language. 198 NSString *localizationName = [pList objectForKey:WebPluginLocalizationNameKey]; 199 if (![localizationName isEqualToString:[[self class] preferredLocalizationName]]) 200 pList = [self pListForPath:pListPath createFile:YES]; 201 MIMETypes = [pList objectForKey:WebPluginMIMETypesKey]; 202 } else 203 // Plist doesn't exist, ask the plug-in to create it. 204 MIMETypes = [[self pListForPath:pListPath createFile:YES] objectForKey:WebPluginMIMETypesKey]; 205 } 206 207 if (!MIMETypes) { 208 MIMETypes = [self _objectForInfoDictionaryKey:WebPluginMIMETypesKey]; 209 if (!MIMETypes) 210 return NO; 211 } 212 213 NSEnumerator *keyEnumerator = [MIMETypes keyEnumerator]; 214 NSDictionary *MIMEDictionary; 215 NSString *MIME, *description; 216 NSArray *extensions; 217 218 while ((MIME = [keyEnumerator nextObject]) != nil) { 219 MIMEDictionary = [MIMETypes objectForKey:MIME]; 220 221 // FIXME: Consider storing disabled MIME types. 222 NSNumber *isEnabled = [MIMEDictionary objectForKey:WebPluginTypeEnabledKey]; 223 if (isEnabled && [isEnabled boolValue] == NO) 224 continue; 225 226 MimeClassInfo mimeClassInfo; 227 228 extensions = [[MIMEDictionary objectForKey:WebPluginExtensionsKey] _web_lowercaseStrings]; 229 for (NSUInteger i = 0; i < [extensions count]; ++i) { 230 // The DivX plug-in lists multiple extensions in a comma separated string instead of using 231 // multiple array elements in the property list. Work around this here by splitting the 232 // extension string into components. 233 NSArray *extensionComponents = [[extensions objectAtIndex:i] componentsSeparatedByString:@","]; 234 235 for (NSString *extension in extensionComponents) 236 mimeClassInfo.extensions.append(extension); 237 } 238 239 if ([extensions count] == 0) 240 extensions = [NSArray arrayWithObject:@""]; 241 242 mimeClassInfo.type = String(MIME).lower(); 243 244 description = [MIMEDictionary objectForKey:WebPluginTypeDescriptionKey]; 245 mimeClassInfo.desc = description; 246 247 pluginInfo.mimes.append(mimeClassInfo); 248 if (!description) 249 description = @""; 250 } 251 252 NSString *filename = [(NSString *)path lastPathComponent]; 253 pluginInfo.file = filename; 254 255 NSString *theName = [self _objectForInfoDictionaryKey:WebPluginNameKey]; 256 if (!theName) 257 theName = filename; 258 pluginInfo.name = theName; 259 260 description = [self _objectForInfoDictionaryKey:WebPluginDescriptionKey]; 261 if (!description) 262 description = filename; 263 pluginInfo.desc = description; 264 265 pluginInfo.isApplicationPlugin = false; 266 267 return YES; 268} 269 270- (BOOL)load 271{ 272 if (cfBundle && !BP_CreatePluginMIMETypesPreferences) 273 BP_CreatePluginMIMETypesPreferences = (BP_CreatePluginMIMETypesPreferencesFuncPtr)CFBundleGetFunctionPointerForName(cfBundle.get(), CFSTR("BP_CreatePluginMIMETypesPreferences")); 274 275 return YES; 276} 277 278- (void)dealloc 279{ 280 ASSERT(!pluginDatabases || [pluginDatabases count] == 0); 281 [pluginDatabases release]; 282 283 [super dealloc]; 284} 285 286- (void)finalize 287{ 288 ASSERT_MAIN_THREAD(); 289 ASSERT(!pluginDatabases || [pluginDatabases count] == 0); 290 [pluginDatabases release]; 291 292 [super finalize]; 293} 294 295- (const String&)path 296{ 297 return path; 298} 299 300- (const PluginInfo&)pluginInfo 301{ 302 return pluginInfo; 303} 304 305- (BOOL)supportsExtension:(const String&)extension 306{ 307 ASSERT(extension.lower() == extension); 308 309 for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) { 310 const Vector<String>& extensions = pluginInfo.mimes[i].extensions; 311 312 if (std::find(extensions.begin(), extensions.end(), extension) != extensions.end()) 313 return YES; 314 } 315 316 return NO; 317} 318 319- (BOOL)supportsMIMEType:(const WTF::String&)mimeType 320{ 321 ASSERT(mimeType.lower() == mimeType); 322 323 for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) { 324 if (pluginInfo.mimes[i].type == mimeType) 325 return YES; 326 } 327 328 return NO; 329} 330 331- (NSString *)MIMETypeForExtension:(const String&)extension 332{ 333 ASSERT(extension.lower() == extension); 334 335 for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) { 336 const MimeClassInfo& mimeClassInfo = pluginInfo.mimes[i]; 337 const Vector<String>& extensions = mimeClassInfo.extensions; 338 339 if (std::find(extensions.begin(), extensions.end(), extension) != extensions.end()) 340 return mimeClassInfo.type; 341 } 342 343 return nil; 344} 345 346- (BOOL)isQuickTimePlugIn 347{ 348 const String& bundleIdentifier = [self bundleIdentifier]; 349 return bundleIdentifier == QuickTimeCocoaPluginIdentifier || bundleIdentifier == QuickTimeCocoaPluginIdentifier; 350} 351 352- (BOOL)isJavaPlugIn 353{ 354 const String& bundleIdentifier = [self bundleIdentifier]; 355 return bundleIdentifier == JavaCocoaPluginIdentifier || bundleIdentifier == JavaCarbonPluginIdentifier; 356} 357 358static inline void swapIntsInHeader(uint32_t* rawData, size_t length) 359{ 360 for (size_t i = 0; i < length; ++i) 361 rawData[i] = OSSwapInt32(rawData[i]); 362} 363 364- (BOOL)isNativeLibraryData:(NSData *)data 365{ 366 NSUInteger sizeInBytes = [data length]; 367 Vector<uint32_t, 128> rawData((sizeInBytes + 3) / 4); 368 memcpy(rawData.data(), [data bytes], sizeInBytes); 369 370 unsigned numArchs = 0; 371 struct fat_arch singleArch = { 0, 0, 0, 0, 0 }; 372 struct fat_arch* archs = 0; 373 374 if (sizeInBytes >= sizeof(struct mach_header_64)) { 375 uint32_t magic = *rawData.data(); 376 377 if (magic == MH_MAGIC || magic == MH_CIGAM) { 378 // We have a 32-bit thin binary 379 struct mach_header* header = (struct mach_header*)rawData.data(); 380 381 // Check if we need to swap the bytes 382 if (magic == MH_CIGAM) 383 swapIntsInHeader(rawData.data(), rawData.size()); 384 385 singleArch.cputype = header->cputype; 386 singleArch.cpusubtype = header->cpusubtype; 387 388 archs = &singleArch; 389 numArchs = 1; 390 } else if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64) { 391 // We have a 64-bit thin binary 392 struct mach_header_64* header = (struct mach_header_64*)rawData.data(); 393 394 // Check if we need to swap the bytes 395 if (magic == MH_CIGAM_64) 396 swapIntsInHeader(rawData.data(), rawData.size()); 397 398 singleArch.cputype = header->cputype; 399 singleArch.cpusubtype = header->cpusubtype; 400 401 archs = &singleArch; 402 numArchs = 1; 403 } else if (magic == FAT_MAGIC || magic == FAT_CIGAM) { 404 // We have a fat (universal) binary 405 406 // Check if we need to swap the bytes 407 if (magic == FAT_CIGAM) 408 swapIntsInHeader(rawData.data(), rawData.size()); 409 410 COMPILE_ASSERT(sizeof(struct fat_header) % sizeof(uint32_t) == 0, struct_fat_header_must_be_integral_size_of_uint32_t); 411 archs = reinterpret_cast<struct fat_arch*>(rawData.data() + sizeof(struct fat_header) / sizeof(uint32_t)); 412 numArchs = reinterpret_cast<struct fat_header*>(rawData.data())->nfat_arch; 413 414 unsigned maxArchs = (sizeInBytes - sizeof(struct fat_header)) / sizeof(struct fat_arch); 415 if (numArchs > maxArchs) 416 numArchs = maxArchs; 417 } 418 } 419 420 if (!archs || !numArchs) 421 return NO; 422 423 const NXArchInfo* localArch = NXGetLocalArchInfo(); 424 if (!localArch) 425 return NO; 426 427 cpu_type_t cputype = localArch->cputype; 428 cpu_subtype_t cpusubtype = localArch->cpusubtype; 429 430#ifdef __x86_64__ 431 // NXGetLocalArchInfo returns CPU_TYPE_X86 even when running in 64-bit. 432 // See <rdar://problem/4996965> for more information. 433 cputype = CPU_TYPE_X86_64; 434#endif 435 436 return NXFindBestFatArch(cputype, cpusubtype, archs, numArchs) != 0; 437} 438 439- (UInt32)versionNumber 440{ 441 // CFBundleGetVersionNumber doesn't work with all possible versioning schemes, but we think for now it's good enough for us. 442 return CFBundleGetVersionNumber(cfBundle.get()); 443} 444 445- (void)wasAddedToPluginDatabase:(WebPluginDatabase *)database 446{ 447 if (!pluginDatabases) 448 pluginDatabases = [[NSMutableSet alloc] init]; 449 450 ASSERT(![pluginDatabases containsObject:database]); 451 [pluginDatabases addObject:database]; 452} 453 454- (void)wasRemovedFromPluginDatabase:(WebPluginDatabase *)database 455{ 456 ASSERT(pluginDatabases); 457 ASSERT([pluginDatabases containsObject:database]); 458 459 [pluginDatabases removeObject:database]; 460} 461 462- (String)bundleIdentifier 463{ 464 return CFBundleGetIdentifier(cfBundle.get()); 465} 466 467- (String)bundleVersion 468{ 469 CFDictionaryRef infoDictionary = CFBundleGetInfoDictionary(cfBundle.get()); 470 if (!infoDictionary) 471 return String(); 472 473 CFTypeRef bundleVersionString = CFDictionaryGetValue(infoDictionary, kCFBundleVersionKey); 474 if (!bundleVersionString || CFGetTypeID(bundleVersionString) != CFStringGetTypeID()) 475 return String(); 476 477 return reinterpret_cast<CFStringRef>(bundleVersionString); 478} 479 480@end 481 482@implementation NSArray (WebPluginExtensions) 483 484- (NSArray *)_web_lowercaseStrings 485{ 486 NSMutableArray *lowercaseStrings = [NSMutableArray arrayWithCapacity:[self count]]; 487 NSEnumerator *strings = [self objectEnumerator]; 488 NSString *string; 489 490 while ((string = [strings nextObject]) != nil) { 491 if ([string isKindOfClass:[NSString class]]) 492 [lowercaseStrings addObject:[string lowercaseString]]; 493 } 494 495 return lowercaseStrings; 496} 497 498@end 499