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