michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: nsPluginsDirDarwin.cpp michael@0: michael@0: Mac OS X implementation of the nsPluginsDir/nsPluginsFile classes. michael@0: michael@0: by Patrick C. Beard. michael@0: */ michael@0: michael@0: #include "GeckoChildProcessHost.h" michael@0: #include "base/process_util.h" michael@0: michael@0: #include "prlink.h" michael@0: #include "prnetdb.h" michael@0: #include "nsXPCOM.h" michael@0: michael@0: #include "nsPluginsDir.h" michael@0: #include "nsNPAPIPlugin.h" michael@0: #include "nsPluginsDirUtils.h" michael@0: michael@0: #include "nsILocalFileMac.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: typedef NS_NPAPIPLUGIN_CALLBACK(const char *, NP_GETMIMEDESCRIPTION) (); michael@0: typedef NS_NPAPIPLUGIN_CALLBACK(OSErr, BP_GETSUPPORTEDMIMETYPES) (BPSupportedMIMETypes *mimeInfo, UInt32 flags); michael@0: michael@0: michael@0: /* michael@0: ** Returns a CFBundleRef if the path refers to a Mac OS X bundle directory. michael@0: ** The caller is responsible for calling CFRelease() to deallocate. michael@0: */ michael@0: static CFBundleRef getPluginBundle(const char* path) michael@0: { michael@0: CFBundleRef bundle = nullptr; michael@0: CFStringRef pathRef = ::CFStringCreateWithCString(nullptr, path, michael@0: kCFStringEncodingUTF8); michael@0: if (pathRef) { michael@0: CFURLRef bundleURL = ::CFURLCreateWithFileSystemPath(nullptr, pathRef, michael@0: kCFURLPOSIXPathStyle, michael@0: true); michael@0: if (bundleURL) { michael@0: bundle = ::CFBundleCreate(nullptr, bundleURL); michael@0: ::CFRelease(bundleURL); michael@0: } michael@0: ::CFRelease(pathRef); michael@0: } michael@0: return bundle; michael@0: } michael@0: michael@0: static nsresult toCFURLRef(nsIFile* file, CFURLRef& outURL) michael@0: { michael@0: nsCOMPtr lfm = do_QueryInterface(file); michael@0: if (!lfm) michael@0: return NS_ERROR_FAILURE; michael@0: CFURLRef url; michael@0: nsresult rv = lfm->GetCFURL(&url); michael@0: if (NS_SUCCEEDED(rv)) michael@0: outURL = url; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: bool nsPluginsDir::IsPluginFile(nsIFile* file) michael@0: { michael@0: nsCString fileName; michael@0: file->GetNativeLeafName(fileName); michael@0: /* michael@0: * Don't load the VDP fake plugin, to avoid tripping a bad bug in OS X michael@0: * 10.5.3 (see bug 436575). michael@0: */ michael@0: if (!strcmp(fileName.get(), "VerifiedDownloadPlugin.plugin")) { michael@0: NS_WARNING("Preventing load of VerifiedDownloadPlugin.plugin (see bug 436575)"); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // Caller is responsible for freeing returned buffer. michael@0: static char* CFStringRefToUTF8Buffer(CFStringRef cfString) michael@0: { michael@0: const char* buffer = ::CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8); michael@0: if (buffer) { michael@0: return PL_strdup(buffer); michael@0: } michael@0: michael@0: int bufferLength = michael@0: ::CFStringGetMaximumSizeForEncoding(::CFStringGetLength(cfString), michael@0: kCFStringEncodingUTF8) + 1; michael@0: char* newBuffer = static_cast(NS_Alloc(bufferLength)); michael@0: if (!newBuffer) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!::CFStringGetCString(cfString, newBuffer, bufferLength, michael@0: kCFStringEncodingUTF8)) { michael@0: NS_Free(newBuffer); michael@0: return nullptr; michael@0: } michael@0: michael@0: newBuffer = static_cast(NS_Realloc(newBuffer, michael@0: strlen(newBuffer) + 1)); michael@0: return newBuffer; michael@0: } michael@0: michael@0: class AutoCFTypeObject { michael@0: public: michael@0: AutoCFTypeObject(CFTypeRef object) michael@0: { michael@0: mObject = object; michael@0: } michael@0: ~AutoCFTypeObject() michael@0: { michael@0: ::CFRelease(mObject); michael@0: } michael@0: private: michael@0: CFTypeRef mObject; michael@0: }; michael@0: michael@0: static Boolean MimeTypeEnabled(CFDictionaryRef mimeDict) { michael@0: if (!mimeDict) { michael@0: return true; michael@0: } michael@0: michael@0: CFTypeRef value; michael@0: if (::CFDictionaryGetValueIfPresent(mimeDict, CFSTR("WebPluginTypeEnabled"), &value)) { michael@0: if (value && ::CFGetTypeID(value) == ::CFBooleanGetTypeID()) { michael@0: return ::CFBooleanGetValue(static_cast(value)); michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static CFDictionaryRef ParsePlistForMIMETypesFilename(CFBundleRef bundle) michael@0: { michael@0: CFTypeRef mimeFileName = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypesFilename")); michael@0: if (!mimeFileName || ::CFGetTypeID(mimeFileName) != ::CFStringGetTypeID()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: FSRef homeDir; michael@0: if (::FSFindFolder(kUserDomain, kCurrentUserFolderType, kDontCreateFolder, &homeDir) != noErr) { michael@0: return nullptr; michael@0: } michael@0: michael@0: CFURLRef userDirURL = ::CFURLCreateFromFSRef(kCFAllocatorDefault, &homeDir); michael@0: if (!userDirURL) { michael@0: return nullptr; michael@0: } michael@0: michael@0: AutoCFTypeObject userDirURLAutorelease(userDirURL); michael@0: CFStringRef mimeFilePath = ::CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("Library/Preferences/%@"), static_cast(mimeFileName)); michael@0: if (!mimeFilePath) { michael@0: return nullptr; michael@0: } michael@0: michael@0: AutoCFTypeObject mimeFilePathAutorelease(mimeFilePath); michael@0: CFURLRef mimeFileURL = ::CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorDefault, mimeFilePath, kCFURLPOSIXPathStyle, false, userDirURL); michael@0: if (!mimeFileURL) { michael@0: return nullptr; michael@0: } michael@0: michael@0: AutoCFTypeObject mimeFileURLAutorelease(mimeFileURL); michael@0: SInt32 errorCode = 0; michael@0: CFDataRef mimeFileData = nullptr; michael@0: Boolean result = ::CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, mimeFileURL, &mimeFileData, nullptr, nullptr, &errorCode); michael@0: if (!result) { michael@0: return nullptr; michael@0: } michael@0: michael@0: AutoCFTypeObject mimeFileDataAutorelease(mimeFileData); michael@0: if (errorCode != 0) { michael@0: return nullptr; michael@0: } michael@0: michael@0: CFPropertyListRef propertyList = ::CFPropertyListCreateFromXMLData(kCFAllocatorDefault, mimeFileData, kCFPropertyListImmutable, nullptr); michael@0: if (!propertyList) { michael@0: return nullptr; michael@0: } michael@0: michael@0: AutoCFTypeObject propertyListAutorelease(propertyList); michael@0: if (::CFGetTypeID(propertyList) != ::CFDictionaryGetTypeID()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: CFTypeRef mimeTypes = ::CFDictionaryGetValue(static_cast(propertyList), CFSTR("WebPluginMIMETypes")); michael@0: if (!mimeTypes || ::CFGetTypeID(mimeTypes) != ::CFDictionaryGetTypeID() || ::CFDictionaryGetCount(static_cast(mimeTypes)) == 0) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return static_cast(::CFRetain(mimeTypes)); michael@0: } michael@0: michael@0: static void ParsePlistPluginInfo(nsPluginInfo& info, CFBundleRef bundle) michael@0: { michael@0: CFDictionaryRef mimeDict = ParsePlistForMIMETypesFilename(bundle); michael@0: michael@0: if (!mimeDict) { michael@0: CFTypeRef mimeTypes = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypes")); michael@0: if (!mimeTypes || ::CFGetTypeID(mimeTypes) != ::CFDictionaryGetTypeID() || ::CFDictionaryGetCount(static_cast(mimeTypes)) == 0) michael@0: return; michael@0: mimeDict = static_cast(::CFRetain(mimeTypes)); michael@0: } michael@0: michael@0: AutoCFTypeObject mimeDictAutorelease(mimeDict); michael@0: int mimeDictKeyCount = ::CFDictionaryGetCount(mimeDict); michael@0: michael@0: // Allocate memory for mime data michael@0: int mimeDataArraySize = mimeDictKeyCount * sizeof(char*); michael@0: info.fMimeTypeArray = static_cast(NS_Alloc(mimeDataArraySize)); michael@0: if (!info.fMimeTypeArray) michael@0: return; michael@0: memset(info.fMimeTypeArray, 0, mimeDataArraySize); michael@0: info.fExtensionArray = static_cast(NS_Alloc(mimeDataArraySize)); michael@0: if (!info.fExtensionArray) michael@0: return; michael@0: memset(info.fExtensionArray, 0, mimeDataArraySize); michael@0: info.fMimeDescriptionArray = static_cast(NS_Alloc(mimeDataArraySize)); michael@0: if (!info.fMimeDescriptionArray) michael@0: return; michael@0: memset(info.fMimeDescriptionArray, 0, mimeDataArraySize); michael@0: michael@0: // Allocate memory for mime dictionary keys and values michael@0: nsAutoArrayPtr keys(new CFTypeRef[mimeDictKeyCount]); michael@0: if (!keys) michael@0: return; michael@0: nsAutoArrayPtr values(new CFTypeRef[mimeDictKeyCount]); michael@0: if (!values) michael@0: return; michael@0: michael@0: info.fVariantCount = 0; michael@0: michael@0: ::CFDictionaryGetKeysAndValues(mimeDict, keys, values); michael@0: for (int i = 0; i < mimeDictKeyCount; i++) { michael@0: CFTypeRef mimeString = keys[i]; michael@0: if (!mimeString || ::CFGetTypeID(mimeString) != ::CFStringGetTypeID()) { michael@0: continue; michael@0: } michael@0: CFTypeRef mimeDict = values[i]; michael@0: if (mimeDict && ::CFGetTypeID(mimeDict) == ::CFDictionaryGetTypeID()) { michael@0: if (!MimeTypeEnabled(static_cast(mimeDict))) { michael@0: continue; michael@0: } michael@0: info.fMimeTypeArray[info.fVariantCount] = CFStringRefToUTF8Buffer(static_cast(mimeString)); michael@0: if (!info.fMimeTypeArray[info.fVariantCount]) { michael@0: continue; michael@0: } michael@0: CFTypeRef extensions = ::CFDictionaryGetValue(static_cast(mimeDict), CFSTR("WebPluginExtensions")); michael@0: if (extensions && ::CFGetTypeID(extensions) == ::CFArrayGetTypeID()) { michael@0: int extensionCount = ::CFArrayGetCount(static_cast(extensions)); michael@0: CFMutableStringRef extensionList = ::CFStringCreateMutable(kCFAllocatorDefault, 0); michael@0: for (int j = 0; j < extensionCount; j++) { michael@0: CFTypeRef extension = ::CFArrayGetValueAtIndex(static_cast(extensions), j); michael@0: if (extension && ::CFGetTypeID(extension) == ::CFStringGetTypeID()) { michael@0: if (j > 0) michael@0: ::CFStringAppend(extensionList, CFSTR(",")); michael@0: ::CFStringAppend(static_cast(extensionList), static_cast(extension)); michael@0: } michael@0: } michael@0: info.fExtensionArray[info.fVariantCount] = CFStringRefToUTF8Buffer(static_cast(extensionList)); michael@0: ::CFRelease(extensionList); michael@0: } michael@0: CFTypeRef description = ::CFDictionaryGetValue(static_cast(mimeDict), CFSTR("WebPluginTypeDescription")); michael@0: if (description && ::CFGetTypeID(description) == ::CFStringGetTypeID()) michael@0: info.fMimeDescriptionArray[info.fVariantCount] = CFStringRefToUTF8Buffer(static_cast(description)); michael@0: } michael@0: info.fVariantCount++; michael@0: } michael@0: } michael@0: michael@0: nsPluginFile::nsPluginFile(nsIFile *spec) michael@0: : mPlugin(spec) michael@0: { michael@0: } michael@0: michael@0: nsPluginFile::~nsPluginFile() {} michael@0: michael@0: nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary) michael@0: { michael@0: if (!mPlugin) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: // 64-bit NSPR does not (yet) support bundles. So in 64-bit builds we need michael@0: // (for now) to load the bundle's executable. However this can cause michael@0: // problems: CFBundleCreate() doesn't run the bundle's executable's michael@0: // initialization code, while NSAddImage() and dlopen() do run it. So using michael@0: // NSPR's dyld loading mechanisms here (NSAddImage() or dlopen()) can cause michael@0: // a bundle's initialization code to run earlier than expected, and lead to michael@0: // crashes. See bug 577967. michael@0: #ifdef __LP64__ michael@0: char executablePath[PATH_MAX]; michael@0: executablePath[0] = '\0'; michael@0: nsAutoCString bundlePath; michael@0: mPlugin->GetNativePath(bundlePath); michael@0: CFStringRef pathRef = ::CFStringCreateWithCString(nullptr, bundlePath.get(), michael@0: kCFStringEncodingUTF8); michael@0: if (pathRef) { michael@0: CFURLRef bundleURL = ::CFURLCreateWithFileSystemPath(nullptr, pathRef, michael@0: kCFURLPOSIXPathStyle, michael@0: true); michael@0: if (bundleURL) { michael@0: CFBundleRef bundle = ::CFBundleCreate(nullptr, bundleURL); michael@0: if (bundle) { michael@0: CFURLRef executableURL = ::CFBundleCopyExecutableURL(bundle); michael@0: if (executableURL) { michael@0: if (!::CFURLGetFileSystemRepresentation(executableURL, true, (UInt8*)&executablePath, PATH_MAX)) michael@0: executablePath[0] = '\0'; michael@0: ::CFRelease(executableURL); michael@0: } michael@0: ::CFRelease(bundle); michael@0: } michael@0: ::CFRelease(bundleURL); michael@0: } michael@0: ::CFRelease(pathRef); michael@0: } michael@0: #else michael@0: nsAutoCString bundlePath; michael@0: mPlugin->GetNativePath(bundlePath); michael@0: const char *executablePath = bundlePath.get(); michael@0: #endif michael@0: michael@0: *outLibrary = PR_LoadLibrary(executablePath); michael@0: pLibrary = *outLibrary; michael@0: if (!pLibrary) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: #ifdef DEBUG michael@0: printf("[loaded plugin %s]\n", bundlePath.get()); michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: static char* p2cstrdup(StringPtr pstr) michael@0: { michael@0: int len = pstr[0]; michael@0: char* cstr = static_cast(NS_Alloc(len + 1)); michael@0: if (cstr) { michael@0: memmove(cstr, pstr + 1, len); michael@0: cstr[len] = '\0'; michael@0: } michael@0: return cstr; michael@0: } michael@0: michael@0: static char* GetNextPluginStringFromHandle(Handle h, short *index) michael@0: { michael@0: char *ret = p2cstrdup((unsigned char*)(*h + *index)); michael@0: *index += (ret ? strlen(ret) : 0) + 1; michael@0: return ret; michael@0: } michael@0: michael@0: static bool IsCompatibleArch(nsIFile *file) michael@0: { michael@0: CFURLRef pluginURL = nullptr; michael@0: if (NS_FAILED(toCFURLRef(file, pluginURL))) michael@0: return false; michael@0: michael@0: bool isPluginFile = false; michael@0: michael@0: CFBundleRef pluginBundle = ::CFBundleCreate(kCFAllocatorDefault, pluginURL); michael@0: if (pluginBundle) { michael@0: UInt32 packageType, packageCreator; michael@0: ::CFBundleGetPackageInfo(pluginBundle, &packageType, &packageCreator); michael@0: if (packageType == 'BRPL' || packageType == 'IEPL' || packageType == 'NSPL') { michael@0: // Get path to plugin as a C string. michael@0: char executablePath[PATH_MAX]; michael@0: executablePath[0] = '\0'; michael@0: if (!::CFURLGetFileSystemRepresentation(pluginURL, true, (UInt8*)&executablePath, PATH_MAX)) { michael@0: executablePath[0] = '\0'; michael@0: } michael@0: michael@0: uint32_t pluginLibArchitectures; michael@0: nsresult rv = mozilla::ipc::GeckoChildProcessHost::GetArchitecturesForBinary(executablePath, &pluginLibArchitectures); michael@0: if (NS_FAILED(rv)) { michael@0: return false; michael@0: } michael@0: michael@0: uint32_t supportedArchitectures = michael@0: #ifdef __LP64__ michael@0: mozilla::ipc::GeckoChildProcessHost::GetSupportedArchitecturesForProcessType(GeckoProcessType_Plugin); michael@0: #else michael@0: base::GetCurrentProcessArchitecture(); michael@0: #endif michael@0: michael@0: // Consider the plugin architecture valid if there is any overlap in the masks. michael@0: isPluginFile = !!(supportedArchitectures & pluginLibArchitectures); michael@0: } michael@0: ::CFRelease(pluginBundle); michael@0: } michael@0: michael@0: ::CFRelease(pluginURL); michael@0: return isPluginFile; michael@0: } michael@0: michael@0: /** michael@0: * Obtains all of the information currently available for this plugin. michael@0: */ michael@0: nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary) michael@0: { michael@0: *outLibrary = nullptr; michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (!IsCompatibleArch(mPlugin)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // clear out the info, except for the first field. michael@0: memset(&info, 0, sizeof(info)); michael@0: michael@0: // Try to get a bundle reference. michael@0: nsAutoCString path; michael@0: if (NS_FAILED(rv = mPlugin->GetNativePath(path))) michael@0: return rv; michael@0: CFBundleRef bundle = getPluginBundle(path.get()); michael@0: michael@0: // fill in full path michael@0: info.fFullPath = PL_strdup(path.get()); michael@0: michael@0: // fill in file name michael@0: nsAutoCString fileName; michael@0: if (NS_FAILED(rv = mPlugin->GetNativeLeafName(fileName))) michael@0: return rv; michael@0: info.fFileName = PL_strdup(fileName.get()); michael@0: michael@0: // Get fName michael@0: if (bundle) { michael@0: CFTypeRef name = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginName")); michael@0: if (name && ::CFGetTypeID(name) == ::CFStringGetTypeID()) michael@0: info.fName = CFStringRefToUTF8Buffer(static_cast(name)); michael@0: } michael@0: michael@0: // Get fDescription michael@0: if (bundle) { michael@0: CFTypeRef description = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginDescription")); michael@0: if (description && ::CFGetTypeID(description) == ::CFStringGetTypeID()) michael@0: info.fDescription = CFStringRefToUTF8Buffer(static_cast(description)); michael@0: } michael@0: michael@0: // Get fVersion michael@0: if (bundle) { michael@0: // Look for the release version first michael@0: CFTypeRef version = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString")); michael@0: if (!version) // try the build version michael@0: version = ::CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey); michael@0: if (version && ::CFGetTypeID(version) == ::CFStringGetTypeID()) michael@0: info.fVersion = CFStringRefToUTF8Buffer(static_cast(version)); michael@0: } michael@0: michael@0: // The last thing we need to do is get MIME data michael@0: // fVariantCount, fMimeTypeArray, fExtensionArray, fMimeDescriptionArray michael@0: michael@0: // First look for data in a bundle plist michael@0: if (bundle) { michael@0: ParsePlistPluginInfo(info, bundle); michael@0: ::CFRelease(bundle); michael@0: if (info.fVariantCount > 0) michael@0: return NS_OK; michael@0: } michael@0: michael@0: // It's possible that our plugin has 2 entry points that'll give us mime type michael@0: // info. Quicktime does this to get around the need of having admin rights to michael@0: // change mime info in the resource fork. We need to use this info instead of michael@0: // the resource. See bug 113464. michael@0: michael@0: // Sadly we have to load the library for this to work. michael@0: rv = LoadPlugin(outLibrary); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // Try to get data from NP_GetMIMEDescription michael@0: if (pLibrary) { michael@0: NP_GETMIMEDESCRIPTION pfnGetMimeDesc = (NP_GETMIMEDESCRIPTION)PR_FindFunctionSymbol(pLibrary, NP_GETMIMEDESCRIPTION_NAME); michael@0: if (pfnGetMimeDesc) michael@0: ParsePluginMimeDescription(pfnGetMimeDesc(), info); michael@0: if (info.fVariantCount) michael@0: return NS_OK; michael@0: } michael@0: michael@0: // We'll fill this in using BP_GetSupportedMIMETypes and/or resource fork data michael@0: BPSupportedMIMETypes mi = {kBPSupportedMIMETypesStructVers_1, nullptr, nullptr}; michael@0: michael@0: // Try to get data from BP_GetSupportedMIMETypes michael@0: if (pLibrary) { michael@0: BP_GETSUPPORTEDMIMETYPES pfnMime = (BP_GETSUPPORTEDMIMETYPES)PR_FindFunctionSymbol(pLibrary, "BP_GetSupportedMIMETypes"); michael@0: if (pfnMime && noErr == pfnMime(&mi, 0) && mi.typeStrings) { michael@0: info.fVariantCount = (**(short**)mi.typeStrings) / 2; michael@0: ::HLock(mi.typeStrings); michael@0: if (mi.infoStrings) // it's possible some plugins have infoStrings missing michael@0: ::HLock(mi.infoStrings); michael@0: } michael@0: } michael@0: michael@0: // Fill in the info struct based on the data in the BPSupportedMIMETypes struct michael@0: int variantCount = info.fVariantCount; michael@0: info.fMimeTypeArray = static_cast(NS_Alloc(variantCount * sizeof(char*))); michael@0: if (!info.fMimeTypeArray) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: info.fExtensionArray = static_cast(NS_Alloc(variantCount * sizeof(char*))); michael@0: if (!info.fExtensionArray) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: if (mi.infoStrings) { michael@0: info.fMimeDescriptionArray = static_cast(NS_Alloc(variantCount * sizeof(char*))); michael@0: if (!info.fMimeDescriptionArray) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: short mimeIndex = 2; michael@0: short descriptionIndex = 2; michael@0: for (int i = 0; i < variantCount; i++) { michael@0: info.fMimeTypeArray[i] = GetNextPluginStringFromHandle(mi.typeStrings, &mimeIndex); michael@0: info.fExtensionArray[i] = GetNextPluginStringFromHandle(mi.typeStrings, &mimeIndex); michael@0: if (mi.infoStrings) michael@0: info.fMimeDescriptionArray[i] = GetNextPluginStringFromHandle(mi.infoStrings, &descriptionIndex); michael@0: } michael@0: michael@0: ::HUnlock(mi.typeStrings); michael@0: ::DisposeHandle(mi.typeStrings); michael@0: if (mi.infoStrings) { michael@0: ::HUnlock(mi.infoStrings); michael@0: ::DisposeHandle(mi.infoStrings); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info) michael@0: { michael@0: NS_Free(info.fName); michael@0: NS_Free(info.fDescription); michael@0: int variantCount = info.fVariantCount; michael@0: for (int i = 0; i < variantCount; i++) { michael@0: NS_Free(info.fMimeTypeArray[i]); michael@0: NS_Free(info.fExtensionArray[i]); michael@0: NS_Free(info.fMimeDescriptionArray[i]); michael@0: } michael@0: NS_Free(info.fMimeTypeArray); michael@0: NS_Free(info.fMimeDescriptionArray); michael@0: NS_Free(info.fExtensionArray); michael@0: NS_Free(info.fFileName); michael@0: NS_Free(info.fFullPath); michael@0: NS_Free(info.fVersion); michael@0: michael@0: return NS_OK; michael@0: }