1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Patrick C. Beard <beard@netscape.com>
24 * Josh Aas <josh@mozilla.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
41 nsPluginsDirDarwin.cpp
43 Mac OS X implementation of the nsPluginsDir/nsPluginsFile classes.
52 #include "nsPluginsDir.h"
53 #include "nsNPAPIPlugin.h"
54 #include "nsPluginsDirUtils.h"
56 #include "nsILocalFileMac.h"
63 #include <Carbon/Carbon.h>
64 #include <CoreServices/CoreServices.h>
65 #include <mach-o/loader.h>
66 #include <mach-o/fat.h>
68 typedef NS_NPAPIPLUGIN_CALLBACK(const char *, NP_GETMIMEDESCRIPTION
) ();
69 typedef NS_NPAPIPLUGIN_CALLBACK(OSErr
, BP_GETSUPPORTEDMIMETYPES
) (BPSupportedMIMETypes
*mimeInfo
, UInt32 flags
);
73 ** Returns a CFBundleRef if the FSSpec refers to a Mac OS X bundle directory.
74 ** The caller is responsible for calling CFRelease() to deallocate.
76 static CFBundleRef
getPluginBundle(const char* path
)
78 CFBundleRef bundle
= NULL
;
79 CFStringRef pathRef
= ::CFStringCreateWithCString(NULL
, path
, kCFStringEncodingUTF8
);
81 CFURLRef bundleURL
= ::CFURLCreateWithFileSystemPath(NULL
, pathRef
, kCFURLPOSIXPathStyle
, true);
83 bundle
= ::CFBundleCreate(NULL
, bundleURL
);
84 ::CFRelease(bundleURL
);
91 static OSErr
toFSSpec(nsIFile
* file
, FSSpec
& outSpec
)
93 nsCOMPtr
<nsILocalFileMac
> lfm
= do_QueryInterface(file
);
103 static nsresult
toCFURLRef(nsIFile
* file
, CFURLRef
& outURL
)
105 nsCOMPtr
<nsILocalFileMac
> lfm
= do_QueryInterface(file
);
107 return NS_ERROR_FAILURE
;
109 nsresult rv
= lfm
->GetCFURL(&url
);
110 if (NS_SUCCEEDED(rv
))
116 // function to test whether or not this is a loadable plugin
117 static PRBool
IsLoadablePlugin(CFURLRef aURL
)
122 PRBool isLoadable
= PR_FALSE
;
124 if (CFURLGetFileSystemRepresentation(aURL
, TRUE
, (UInt8
*)path
, sizeof(path
))) {
126 int f
= open(path
, O_RDONLY
);
128 // Mach-O headers use the byte ordering of the architecture on which
129 // they run, so test against the magic number in the byte order
130 // we're compiling for. Fat headers are always big-endian, so swap
131 // them to host before comparing to host representation of the magic
132 if (read(f
, &magic
, sizeof(magic
)) == sizeof(magic
)) {
133 if ((magic
== MH_MAGIC
) || (PR_ntohl(magic
) == FAT_MAGIC
))
134 isLoadable
= PR_TRUE
;
142 PRBool
nsPluginsDir::IsPluginFile(nsIFile
* file
)
145 file
->GetNativeLeafName(temp
);
147 * Don't load the VDP fake plugin, to avoid tripping a bad bug in OS X
148 * 10.5.3 (see bug 436575).
150 if (!strcmp(temp
.get(), "VerifiedDownloadPlugin.plugin")) {
151 NS_WARNING("Preventing load of VerifiedDownloadPlugin.plugin (see bug 436575)");
155 CFURLRef pluginURL
= NULL
;
156 if (NS_FAILED(toCFURLRef(file
, pluginURL
)))
159 PRBool isPluginFile
= PR_FALSE
;
161 CFBundleRef pluginBundle
= CFBundleCreate(kCFAllocatorDefault
, pluginURL
);
163 UInt32 packageType
, packageCreator
;
164 CFBundleGetPackageInfo(pluginBundle
, &packageType
, &packageCreator
);
165 if (packageType
== 'BRPL' || packageType
== 'IEPL' || packageType
== 'NSPL') {
166 CFURLRef executableURL
= CFBundleCopyExecutableURL(pluginBundle
);
168 isPluginFile
= IsLoadablePlugin(executableURL
);
169 ::CFRelease(executableURL
);
172 ::CFRelease(pluginBundle
);
175 LSItemInfoRecord info
;
176 if (LSCopyItemInfoForURL(pluginURL
, kLSRequestTypeCreator
, &info
) == noErr
) {
177 if ((info
.filetype
== 'shlb' && info
.creator
== 'MOSS') ||
178 info
.filetype
== 'NSPL' ||
179 info
.filetype
== 'BRPL' ||
180 info
.filetype
== 'IEPL') {
181 isPluginFile
= IsLoadablePlugin(pluginURL
);
186 ::CFRelease(pluginURL
);
190 // Caller is responsible for freeing returned buffer.
191 static char* CFStringRefToUTF8Buffer(CFStringRef cfString
)
193 int bufferLength
= ::CFStringGetLength(cfString
) + 1;
194 char* newBuffer
= static_cast<char*>(NS_Alloc(bufferLength
));
195 if (newBuffer
&& !::CFStringGetCString(cfString
, newBuffer
, bufferLength
, kCFStringEncodingUTF8
)) {
202 static void ParsePlistPluginInfo(nsPluginInfo
& info
, CFBundleRef bundle
)
204 CFTypeRef mimeDict
= ::CFBundleGetValueForInfoDictionaryKey(bundle
, CFSTR("WebPluginMIMETypes"));
205 if (mimeDict
&& ::CFGetTypeID(mimeDict
) == ::CFDictionaryGetTypeID() && ::CFDictionaryGetCount(static_cast<CFDictionaryRef
>(mimeDict
)) > 0) {
206 int mimeDictKeyCount
= ::CFDictionaryGetCount(static_cast<CFDictionaryRef
>(mimeDict
));
208 // Allocate memory for mime data
209 int mimeDataArraySize
= mimeDictKeyCount
* sizeof(char*);
210 info
.fMimeTypeArray
= static_cast<char**>(NS_Alloc(mimeDataArraySize
));
211 if (!info
.fMimeTypeArray
)
213 memset(info
.fMimeTypeArray
, 0, mimeDataArraySize
);
214 info
.fExtensionArray
= static_cast<char**>(NS_Alloc(mimeDataArraySize
));
215 if (!info
.fExtensionArray
)
217 memset(info
.fExtensionArray
, 0, mimeDataArraySize
);
218 info
.fMimeDescriptionArray
= static_cast<char**>(NS_Alloc(mimeDataArraySize
));
219 if (!info
.fMimeDescriptionArray
)
221 memset(info
.fMimeDescriptionArray
, 0, mimeDataArraySize
);
223 // Allocate memory for mime dictionary keys and values
224 nsAutoArrayPtr
<CFTypeRef
> keys(new CFTypeRef
[mimeDictKeyCount
]);
227 nsAutoArrayPtr
<CFTypeRef
> values(new CFTypeRef
[mimeDictKeyCount
]);
231 // Set the variant count now that we have safely allocated memory
232 info
.fVariantCount
= mimeDictKeyCount
;
234 ::CFDictionaryGetKeysAndValues(static_cast<CFDictionaryRef
>(mimeDict
), keys
, values
);
235 for (int i
= 0; i
< mimeDictKeyCount
; i
++) {
236 CFTypeRef mimeString
= keys
[i
];
237 if (mimeString
&& ::CFGetTypeID(mimeString
) == ::CFStringGetTypeID()) {
238 info
.fMimeTypeArray
[i
] = CFStringRefToUTF8Buffer(static_cast<CFStringRef
>(mimeString
));
241 info
.fVariantCount
-= 1;
244 CFTypeRef mimeDict
= values
[i
];
245 if (mimeDict
&& ::CFGetTypeID(mimeDict
) == ::CFDictionaryGetTypeID()) {
246 CFTypeRef extensions
= ::CFDictionaryGetValue(static_cast<CFDictionaryRef
>(mimeDict
), CFSTR("WebPluginExtensions"));
247 if (extensions
&& ::CFGetTypeID(extensions
) == ::CFArrayGetTypeID()) {
248 int extensionCount
= ::CFArrayGetCount(static_cast<CFArrayRef
>(extensions
));
249 CFMutableStringRef extensionList
= ::CFStringCreateMutable(kCFAllocatorDefault
, 0);
250 for (int j
= 0; j
< extensionCount
; j
++) {
251 CFTypeRef extension
= ::CFArrayGetValueAtIndex(static_cast<CFArrayRef
>(extensions
), j
);
252 if (extension
&& ::CFGetTypeID(extension
) == ::CFStringGetTypeID()) {
254 ::CFStringAppend(extensionList
, CFSTR(","));
255 ::CFStringAppend(static_cast<CFMutableStringRef
>(extensionList
), static_cast<CFStringRef
>(extension
));
258 info
.fExtensionArray
[i
] = CFStringRefToUTF8Buffer(static_cast<CFStringRef
>(extensionList
));
259 ::CFRelease(extensionList
);
261 CFTypeRef description
= ::CFDictionaryGetValue(static_cast<CFDictionaryRef
>(mimeDict
), CFSTR("WebPluginTypeDescription"));
262 if (description
&& ::CFGetTypeID(description
) == ::CFStringGetTypeID())
263 info
.fMimeDescriptionArray
[i
] = CFStringRefToUTF8Buffer(static_cast<CFStringRef
>(description
));
269 nsPluginFile::nsPluginFile(nsIFile
*spec
)
274 nsPluginFile::~nsPluginFile() {}
277 * Loads the plugin into memory using NSPR's shared-library loading
278 * mechanism. Handles platform differences in loading shared libraries.
280 nsresult
nsPluginFile::LoadPlugin(PRLibrary
* &outLibrary
)
285 return NS_ERROR_NULL_POINTER
;
288 mPlugin
->GetNativePath(temp
);
291 outLibrary
= PR_LoadLibrary(path
);
292 pLibrary
= outLibrary
;
294 return NS_ERROR_FAILURE
;
297 printf("[loaded plugin %s]\n", path
);
302 static char* p2cstrdup(StringPtr pstr
)
305 char* cstr
= static_cast<char*>(NS_Alloc(len
+ 1));
307 ::BlockMoveData(pstr
+ 1, cstr
, len
);
313 static char* GetNextPluginStringFromHandle(Handle h
, short *index
)
315 char *ret
= p2cstrdup((unsigned char*)(*h
+ *index
));
316 *index
+= (ret
? PL_strlen(ret
) : 0) + 1;
320 static char* GetPluginString(short id
, short index
)
323 ::GetIndString(str
, id
, index
);
324 return p2cstrdup(str
);
327 // Opens the resource fork for the plugin
328 // Also checks if the plugin is a CFBundle and opens gets the correct resource
329 static short OpenPluginResourceFork(nsIFile
*pluginFile
)
332 OSErr err
= toFSSpec(pluginFile
, spec
);
333 Boolean targetIsFolder
, wasAliased
;
334 err
= ::ResolveAliasFile(&spec
, true, &targetIsFolder
, &wasAliased
);
335 short refNum
= ::FSpOpenResFile(&spec
, fsRdPerm
);
338 pluginFile
->GetNativePath(path
);
339 CFBundleRef bundle
= getPluginBundle(path
.get());
341 refNum
= CFBundleOpenBundleResourceMap(bundle
);
348 short nsPluginFile::OpenPluginResource()
350 return OpenPluginResourceFork(mPlugin
);
353 class nsAutoCloseResourceObject
{
355 nsAutoCloseResourceObject(nsIFile
*pluginFile
)
357 mRefNum
= OpenPluginResourceFork(pluginFile
);
359 ~nsAutoCloseResourceObject()
362 ::CloseResFile(mRefNum
);
364 PRBool
ResourceOpened()
366 return (mRefNum
> 0);
373 * Obtains all of the information currently available for this plugin.
375 nsresult
nsPluginFile::GetPluginInfo(nsPluginInfo
& info
)
377 // clear out the info, except for the first field.
378 memset(&info
, 0, sizeof(info
));
380 // First open up resource we can use to get plugin info.
382 // Try to open a resource fork.
383 nsAutoCloseResourceObject
resourceObject(mPlugin
);
384 bool resourceOpened
= resourceObject
.ResourceOpened();
385 // Try to get a bundle reference.
387 mPlugin
->GetNativePath(path
);
388 CFBundleRef bundle
= getPluginBundle(path
.get());
392 info
.fBundle
= PR_TRUE
;
396 CFTypeRef name
= ::CFBundleGetValueForInfoDictionaryKey(bundle
, CFSTR("WebPluginName"));
397 if (name
&& ::CFGetTypeID(name
) == ::CFStringGetTypeID())
398 info
.fName
= CFStringRefToUTF8Buffer(static_cast<CFStringRef
>(name
));
400 if (!info
.fName
&& resourceOpened
) {
401 // 'STR#', 126, 2 => plugin name.
402 info
.fName
= GetPluginString(126, 2);
407 CFTypeRef description
= ::CFBundleGetValueForInfoDictionaryKey(bundle
, CFSTR("WebPluginDescription"));
408 if (description
&& ::CFGetTypeID(description
) == ::CFStringGetTypeID())
409 info
.fDescription
= CFStringRefToUTF8Buffer(static_cast<CFStringRef
>(description
));
411 if (!info
.fDescription
&& resourceOpened
) {
412 // 'STR#', 126, 1 => plugin description.
413 info
.fDescription
= GetPluginString(126, 1);
418 toFSSpec(mPlugin
, spec
);
419 info
.fFileName
= p2cstrdup(spec
.name
);
422 info
.fFullPath
= PL_strdup(path
.get());
426 // Look for the release version first
427 CFTypeRef version
= ::CFBundleGetValueForInfoDictionaryKey(bundle
, CFSTR("CFBundleShortVersionString"));
428 if (!version
) // try the build version
429 version
= ::CFBundleGetValueForInfoDictionaryKey(bundle
, kCFBundleVersionKey
);
430 if (version
&& ::CFGetTypeID(version
) == ::CFStringGetTypeID())
431 info
.fVersion
= CFStringRefToUTF8Buffer(static_cast<CFStringRef
>(version
));
434 // The last thing we need to do is get MIME data
435 // fVariantCount, fMimeTypeArray, fExtensionArray, fMimeDescriptionArray
437 // First look for data in a bundle plist
439 ParsePlistPluginInfo(info
, bundle
);
441 if (info
.fVariantCount
> 0)
445 // It's possible that our plugin has 2 entry points that'll give us mime type
446 // info. Quicktime does this to get around the need of having admin rights to
447 // change mime info in the resource fork. We need to use this info instead of
448 // the resource. See bug 113464.
450 // Try to get data from NP_GetMIMEDescription
452 NP_GETMIMEDESCRIPTION pfnGetMimeDesc
= (NP_GETMIMEDESCRIPTION
)PR_FindFunctionSymbol(pLibrary
, NP_GETMIMEDESCRIPTION_NAME
);
454 ParsePluginMimeDescription(pfnGetMimeDesc(), info
);
455 if (info
.fVariantCount
)
459 // We'll fill this in using BP_GetSupportedMIMETypes and/or resource fork data
460 BPSupportedMIMETypes mi
= {kBPSupportedMIMETypesStructVers_1
, NULL
, NULL
};
462 // Try to get data from BP_GetSupportedMIMETypes
464 BP_GETSUPPORTEDMIMETYPES pfnMime
= (BP_GETSUPPORTEDMIMETYPES
)PR_FindFunctionSymbol(pLibrary
, "BP_GetSupportedMIMETypes");
465 if (pfnMime
&& noErr
== pfnMime(&mi
, 0) && mi
.typeStrings
) {
466 info
.fVariantCount
= (**(short**)mi
.typeStrings
) / 2;
467 ::HLock(mi
.typeStrings
);
468 if (mi
.infoStrings
) // it's possible some plugins have infoStrings missing
469 ::HLock(mi
.infoStrings
);
473 // Try to get data from the resource fork
474 if (!info
.fVariantCount
&& resourceObject
.ResourceOpened()) {
475 mi
.typeStrings
= ::Get1Resource('STR#', 128);
476 if (mi
.typeStrings
) {
477 info
.fVariantCount
= (**(short**)mi
.typeStrings
) / 2;
478 ::DetachResource(mi
.typeStrings
);
479 ::HLock(mi
.typeStrings
);
481 // Don't add this plugin because no mime types could be found
482 return NS_ERROR_FAILURE
;
485 mi
.infoStrings
= ::Get1Resource('STR#', 127);
486 if (mi
.infoStrings
) {
487 ::DetachResource(mi
.infoStrings
);
488 ::HLock(mi
.infoStrings
);
492 // Fill in the info struct based on the data in the BPSupportedMIMETypes struct
493 int variantCount
= info
.fVariantCount
;
494 info
.fMimeTypeArray
= static_cast<char**>(NS_Alloc(variantCount
* sizeof(char*)));
495 if (!info
.fMimeTypeArray
)
496 return NS_ERROR_OUT_OF_MEMORY
;
497 info
.fExtensionArray
= static_cast<char**>(NS_Alloc(variantCount
* sizeof(char*)));
498 if (!info
.fExtensionArray
)
499 return NS_ERROR_OUT_OF_MEMORY
;
500 if (mi
.infoStrings
) {
501 info
.fMimeDescriptionArray
= static_cast<char**>(NS_Alloc(variantCount
* sizeof(char*)));
502 if (!info
.fMimeDescriptionArray
)
503 return NS_ERROR_OUT_OF_MEMORY
;
506 short descriptionIndex
= 2;
507 for (int i
= 0; i
< variantCount
; i
++) {
508 info
.fMimeTypeArray
[i
] = GetNextPluginStringFromHandle(mi
.typeStrings
, &mimeIndex
);
509 info
.fExtensionArray
[i
] = GetNextPluginStringFromHandle(mi
.typeStrings
, &mimeIndex
);
511 info
.fMimeDescriptionArray
[i
] = GetNextPluginStringFromHandle(mi
.infoStrings
, &descriptionIndex
);
514 ::HUnlock(mi
.typeStrings
);
515 ::DisposeHandle(mi
.typeStrings
);
516 if (mi
.infoStrings
) {
517 ::HUnlock(mi
.infoStrings
);
518 ::DisposeHandle(mi
.infoStrings
);
524 nsresult
nsPluginFile::FreePluginInfo(nsPluginInfo
& info
)
527 NS_Free(info
.fDescription
);
528 int variantCount
= info
.fVariantCount
;
529 for (int i
= 0; i
< variantCount
; i
++) {
530 NS_Free(info
.fMimeTypeArray
[i
]);
531 NS_Free(info
.fExtensionArray
[i
]);
532 NS_Free(info
.fMimeDescriptionArray
[i
]);
534 NS_Free(info
.fMimeTypeArray
);
535 NS_Free(info
.fMimeDescriptionArray
);
536 NS_Free(info
.fExtensionArray
);
537 NS_Free(info
.fFileName
);
538 NS_Free(info
.fFullPath
);
539 NS_Free(info
.fVersion
);