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