dom/plugins/base/nsPluginsDirDarwin.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /*
     7   nsPluginsDirDarwin.cpp
     9   Mac OS X implementation of the nsPluginsDir/nsPluginsFile classes.
    11   by Patrick C. Beard.
    12  */
    14 #include "GeckoChildProcessHost.h"
    15 #include "base/process_util.h"
    17 #include "prlink.h"
    18 #include "prnetdb.h"
    19 #include "nsXPCOM.h"
    21 #include "nsPluginsDir.h"
    22 #include "nsNPAPIPlugin.h"
    23 #include "nsPluginsDirUtils.h"
    25 #include "nsILocalFileMac.h"
    27 #include <string.h>
    28 #include <stdio.h>
    29 #include <unistd.h>
    30 #include <fcntl.h>
    32 #include <Carbon/Carbon.h>
    33 #include <CoreServices/CoreServices.h>
    34 #include <mach-o/loader.h>
    35 #include <mach-o/fat.h>
    37 typedef NS_NPAPIPLUGIN_CALLBACK(const char *, NP_GETMIMEDESCRIPTION) ();
    38 typedef NS_NPAPIPLUGIN_CALLBACK(OSErr, BP_GETSUPPORTEDMIMETYPES) (BPSupportedMIMETypes *mimeInfo, UInt32 flags);
    41 /*
    42 ** Returns a CFBundleRef if the path refers to a Mac OS X bundle directory.
    43 ** The caller is responsible for calling CFRelease() to deallocate.
    44 */
    45 static CFBundleRef getPluginBundle(const char* path)
    46 {
    47   CFBundleRef bundle = nullptr;
    48   CFStringRef pathRef = ::CFStringCreateWithCString(nullptr, path,
    49                                                     kCFStringEncodingUTF8);
    50   if (pathRef) {
    51     CFURLRef bundleURL = ::CFURLCreateWithFileSystemPath(nullptr, pathRef,
    52                                                          kCFURLPOSIXPathStyle,
    53                                                          true);
    54     if (bundleURL) {
    55       bundle = ::CFBundleCreate(nullptr, bundleURL);
    56       ::CFRelease(bundleURL);
    57     }
    58     ::CFRelease(pathRef);
    59   }
    60   return bundle;
    61 }
    63 static nsresult toCFURLRef(nsIFile* file, CFURLRef& outURL)
    64 {
    65   nsCOMPtr<nsILocalFileMac> lfm = do_QueryInterface(file);
    66   if (!lfm)
    67     return NS_ERROR_FAILURE;
    68   CFURLRef url;
    69   nsresult rv = lfm->GetCFURL(&url);
    70   if (NS_SUCCEEDED(rv))
    71     outURL = url;
    73   return rv;
    74 }
    76 bool nsPluginsDir::IsPluginFile(nsIFile* file)
    77 {
    78   nsCString fileName;
    79   file->GetNativeLeafName(fileName);
    80   /*
    81    * Don't load the VDP fake plugin, to avoid tripping a bad bug in OS X
    82    * 10.5.3 (see bug 436575).
    83    */
    84   if (!strcmp(fileName.get(), "VerifiedDownloadPlugin.plugin")) {
    85     NS_WARNING("Preventing load of VerifiedDownloadPlugin.plugin (see bug 436575)");
    86     return false;
    87   }
    88   return true;
    89 }
    91 // Caller is responsible for freeing returned buffer.
    92 static char* CFStringRefToUTF8Buffer(CFStringRef cfString)
    93 {
    94   const char* buffer = ::CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8);
    95   if (buffer) {
    96     return PL_strdup(buffer);
    97   }
    99   int bufferLength =
   100     ::CFStringGetMaximumSizeForEncoding(::CFStringGetLength(cfString),
   101                                         kCFStringEncodingUTF8) + 1;
   102   char* newBuffer = static_cast<char*>(NS_Alloc(bufferLength));
   103   if (!newBuffer) {
   104     return nullptr;
   105   }
   107   if (!::CFStringGetCString(cfString, newBuffer, bufferLength,
   108                             kCFStringEncodingUTF8)) {
   109     NS_Free(newBuffer);
   110     return nullptr;
   111   }
   113   newBuffer = static_cast<char*>(NS_Realloc(newBuffer,
   114                                             strlen(newBuffer) + 1));
   115   return newBuffer;
   116 }
   118 class AutoCFTypeObject {
   119 public:
   120   AutoCFTypeObject(CFTypeRef object)
   121   {
   122     mObject = object;
   123   }
   124   ~AutoCFTypeObject()
   125   {
   126     ::CFRelease(mObject);
   127   }
   128 private:
   129   CFTypeRef mObject;
   130 };
   132 static Boolean MimeTypeEnabled(CFDictionaryRef mimeDict) {
   133   if (!mimeDict) {
   134     return true;
   135   }
   137   CFTypeRef value;
   138   if (::CFDictionaryGetValueIfPresent(mimeDict, CFSTR("WebPluginTypeEnabled"), &value)) {
   139     if (value && ::CFGetTypeID(value) == ::CFBooleanGetTypeID()) {
   140       return ::CFBooleanGetValue(static_cast<CFBooleanRef>(value));
   141     }
   142   }
   143   return true;
   144 }
   146 static CFDictionaryRef ParsePlistForMIMETypesFilename(CFBundleRef bundle)
   147 {
   148   CFTypeRef mimeFileName = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypesFilename"));
   149   if (!mimeFileName || ::CFGetTypeID(mimeFileName) != ::CFStringGetTypeID()) {
   150     return nullptr;
   151   }
   153   FSRef homeDir;
   154   if (::FSFindFolder(kUserDomain, kCurrentUserFolderType, kDontCreateFolder, &homeDir) != noErr) {
   155     return nullptr;
   156   }
   158   CFURLRef userDirURL = ::CFURLCreateFromFSRef(kCFAllocatorDefault, &homeDir);
   159   if (!userDirURL) {
   160     return nullptr;
   161   }
   163   AutoCFTypeObject userDirURLAutorelease(userDirURL);
   164   CFStringRef mimeFilePath = ::CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("Library/Preferences/%@"), static_cast<CFStringRef>(mimeFileName));
   165   if (!mimeFilePath) {
   166     return nullptr;
   167   }
   169   AutoCFTypeObject mimeFilePathAutorelease(mimeFilePath);
   170   CFURLRef mimeFileURL = ::CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorDefault, mimeFilePath, kCFURLPOSIXPathStyle, false, userDirURL);
   171   if (!mimeFileURL) {
   172     return nullptr;
   173   }
   175   AutoCFTypeObject mimeFileURLAutorelease(mimeFileURL);
   176   SInt32 errorCode = 0;
   177   CFDataRef mimeFileData = nullptr;
   178   Boolean result = ::CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, mimeFileURL, &mimeFileData, nullptr, nullptr, &errorCode);
   179   if (!result) {
   180     return nullptr;
   181   }
   183   AutoCFTypeObject mimeFileDataAutorelease(mimeFileData);
   184   if (errorCode != 0) {
   185     return nullptr;
   186   }
   188   CFPropertyListRef propertyList = ::CFPropertyListCreateFromXMLData(kCFAllocatorDefault, mimeFileData, kCFPropertyListImmutable, nullptr);
   189   if (!propertyList) {
   190     return nullptr;
   191   }
   193   AutoCFTypeObject propertyListAutorelease(propertyList);
   194   if (::CFGetTypeID(propertyList) != ::CFDictionaryGetTypeID()) {
   195     return nullptr;
   196   }
   198   CFTypeRef mimeTypes = ::CFDictionaryGetValue(static_cast<CFDictionaryRef>(propertyList), CFSTR("WebPluginMIMETypes"));
   199   if (!mimeTypes || ::CFGetTypeID(mimeTypes) != ::CFDictionaryGetTypeID() || ::CFDictionaryGetCount(static_cast<CFDictionaryRef>(mimeTypes)) == 0) {
   200     return nullptr;
   201   }
   203   return static_cast<CFDictionaryRef>(::CFRetain(mimeTypes));
   204 }
   206 static void ParsePlistPluginInfo(nsPluginInfo& info, CFBundleRef bundle)
   207 {
   208   CFDictionaryRef mimeDict = ParsePlistForMIMETypesFilename(bundle);
   210   if (!mimeDict) {
   211     CFTypeRef mimeTypes = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypes"));
   212     if (!mimeTypes || ::CFGetTypeID(mimeTypes) != ::CFDictionaryGetTypeID() || ::CFDictionaryGetCount(static_cast<CFDictionaryRef>(mimeTypes)) == 0)
   213       return;
   214     mimeDict = static_cast<CFDictionaryRef>(::CFRetain(mimeTypes));
   215   }
   217   AutoCFTypeObject mimeDictAutorelease(mimeDict);
   218   int mimeDictKeyCount = ::CFDictionaryGetCount(mimeDict);
   220   // Allocate memory for mime data
   221   int mimeDataArraySize = mimeDictKeyCount * sizeof(char*);
   222   info.fMimeTypeArray = static_cast<char**>(NS_Alloc(mimeDataArraySize));
   223   if (!info.fMimeTypeArray)
   224     return;
   225   memset(info.fMimeTypeArray, 0, mimeDataArraySize);
   226   info.fExtensionArray = static_cast<char**>(NS_Alloc(mimeDataArraySize));
   227   if (!info.fExtensionArray)
   228     return;
   229   memset(info.fExtensionArray, 0, mimeDataArraySize);
   230   info.fMimeDescriptionArray = static_cast<char**>(NS_Alloc(mimeDataArraySize));
   231   if (!info.fMimeDescriptionArray)
   232     return;
   233   memset(info.fMimeDescriptionArray, 0, mimeDataArraySize);
   235   // Allocate memory for mime dictionary keys and values
   236   nsAutoArrayPtr<CFTypeRef> keys(new CFTypeRef[mimeDictKeyCount]);
   237   if (!keys)
   238     return;
   239   nsAutoArrayPtr<CFTypeRef> values(new CFTypeRef[mimeDictKeyCount]);
   240   if (!values)
   241     return;
   243   info.fVariantCount = 0;
   245   ::CFDictionaryGetKeysAndValues(mimeDict, keys, values);
   246   for (int i = 0; i < mimeDictKeyCount; i++) {
   247     CFTypeRef mimeString = keys[i];
   248     if (!mimeString || ::CFGetTypeID(mimeString) != ::CFStringGetTypeID()) {
   249       continue;
   250     }
   251     CFTypeRef mimeDict = values[i];
   252     if (mimeDict && ::CFGetTypeID(mimeDict) == ::CFDictionaryGetTypeID()) {
   253       if (!MimeTypeEnabled(static_cast<CFDictionaryRef>(mimeDict))) {
   254         continue;
   255       }
   256       info.fMimeTypeArray[info.fVariantCount] = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(mimeString));
   257       if (!info.fMimeTypeArray[info.fVariantCount]) {
   258         continue;
   259       }
   260       CFTypeRef extensions = ::CFDictionaryGetValue(static_cast<CFDictionaryRef>(mimeDict), CFSTR("WebPluginExtensions"));
   261       if (extensions && ::CFGetTypeID(extensions) == ::CFArrayGetTypeID()) {
   262         int extensionCount = ::CFArrayGetCount(static_cast<CFArrayRef>(extensions));
   263         CFMutableStringRef extensionList = ::CFStringCreateMutable(kCFAllocatorDefault, 0);
   264         for (int j = 0; j < extensionCount; j++) {
   265           CFTypeRef extension = ::CFArrayGetValueAtIndex(static_cast<CFArrayRef>(extensions), j);
   266           if (extension && ::CFGetTypeID(extension) == ::CFStringGetTypeID()) {
   267             if (j > 0)
   268               ::CFStringAppend(extensionList, CFSTR(","));
   269             ::CFStringAppend(static_cast<CFMutableStringRef>(extensionList), static_cast<CFStringRef>(extension));
   270           }
   271         }
   272         info.fExtensionArray[info.fVariantCount] = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(extensionList));
   273         ::CFRelease(extensionList);
   274       }
   275       CFTypeRef description = ::CFDictionaryGetValue(static_cast<CFDictionaryRef>(mimeDict), CFSTR("WebPluginTypeDescription"));
   276       if (description && ::CFGetTypeID(description) == ::CFStringGetTypeID())
   277         info.fMimeDescriptionArray[info.fVariantCount] = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(description));
   278     }
   279     info.fVariantCount++;
   280   }
   281 }
   283 nsPluginFile::nsPluginFile(nsIFile *spec)
   284     : mPlugin(spec)
   285 {
   286 }
   288 nsPluginFile::~nsPluginFile() {}
   290 nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary)
   291 {
   292   if (!mPlugin)
   293     return NS_ERROR_NULL_POINTER;
   295   // 64-bit NSPR does not (yet) support bundles.  So in 64-bit builds we need
   296   // (for now) to load the bundle's executable.  However this can cause
   297   // problems:  CFBundleCreate() doesn't run the bundle's executable's
   298   // initialization code, while NSAddImage() and dlopen() do run it.  So using
   299   // NSPR's dyld loading mechanisms here (NSAddImage() or dlopen()) can cause
   300   // a bundle's initialization code to run earlier than expected, and lead to
   301   // crashes.  See bug 577967.
   302 #ifdef __LP64__
   303   char executablePath[PATH_MAX];
   304   executablePath[0] = '\0';
   305   nsAutoCString bundlePath;
   306   mPlugin->GetNativePath(bundlePath);
   307   CFStringRef pathRef = ::CFStringCreateWithCString(nullptr, bundlePath.get(),
   308                                                     kCFStringEncodingUTF8);
   309   if (pathRef) {
   310     CFURLRef bundleURL = ::CFURLCreateWithFileSystemPath(nullptr, pathRef,
   311                                                          kCFURLPOSIXPathStyle,
   312                                                          true);
   313     if (bundleURL) {
   314       CFBundleRef bundle = ::CFBundleCreate(nullptr, bundleURL);
   315       if (bundle) {
   316         CFURLRef executableURL = ::CFBundleCopyExecutableURL(bundle);
   317         if (executableURL) {
   318           if (!::CFURLGetFileSystemRepresentation(executableURL, true, (UInt8*)&executablePath, PATH_MAX))
   319             executablePath[0] = '\0';
   320           ::CFRelease(executableURL);
   321         }
   322         ::CFRelease(bundle);
   323       }
   324       ::CFRelease(bundleURL);
   325     }
   326     ::CFRelease(pathRef); 
   327   }
   328 #else
   329   nsAutoCString bundlePath;
   330   mPlugin->GetNativePath(bundlePath);
   331   const char *executablePath = bundlePath.get();
   332 #endif
   334   *outLibrary = PR_LoadLibrary(executablePath);
   335   pLibrary = *outLibrary;
   336   if (!pLibrary) {
   337     return NS_ERROR_FAILURE;
   338   }
   339 #ifdef DEBUG
   340   printf("[loaded plugin %s]\n", bundlePath.get());
   341 #endif
   342   return NS_OK;
   343 }
   345 static char* p2cstrdup(StringPtr pstr)
   346 {
   347   int len = pstr[0];
   348   char* cstr = static_cast<char*>(NS_Alloc(len + 1));
   349   if (cstr) {
   350     memmove(cstr, pstr + 1, len);
   351     cstr[len] = '\0';
   352   }
   353   return cstr;
   354 }
   356 static char* GetNextPluginStringFromHandle(Handle h, short *index)
   357 {
   358   char *ret = p2cstrdup((unsigned char*)(*h + *index));
   359   *index += (ret ? strlen(ret) : 0) + 1;
   360   return ret;
   361 }
   363 static bool IsCompatibleArch(nsIFile *file)
   364 {
   365   CFURLRef pluginURL = nullptr;
   366   if (NS_FAILED(toCFURLRef(file, pluginURL)))
   367     return false;
   369   bool isPluginFile = false;
   371   CFBundleRef pluginBundle = ::CFBundleCreate(kCFAllocatorDefault, pluginURL);
   372   if (pluginBundle) {
   373     UInt32 packageType, packageCreator;
   374     ::CFBundleGetPackageInfo(pluginBundle, &packageType, &packageCreator);
   375     if (packageType == 'BRPL' || packageType == 'IEPL' || packageType == 'NSPL') {
   376       // Get path to plugin as a C string.
   377       char executablePath[PATH_MAX];
   378       executablePath[0] = '\0';
   379       if (!::CFURLGetFileSystemRepresentation(pluginURL, true, (UInt8*)&executablePath, PATH_MAX)) {
   380         executablePath[0] = '\0';
   381       }
   383       uint32_t pluginLibArchitectures;
   384       nsresult rv = mozilla::ipc::GeckoChildProcessHost::GetArchitecturesForBinary(executablePath, &pluginLibArchitectures);
   385       if (NS_FAILED(rv)) {
   386         return false;
   387       }
   389       uint32_t supportedArchitectures =
   390 #ifdef __LP64__
   391           mozilla::ipc::GeckoChildProcessHost::GetSupportedArchitecturesForProcessType(GeckoProcessType_Plugin);
   392 #else
   393           base::GetCurrentProcessArchitecture();
   394 #endif
   396       // Consider the plugin architecture valid if there is any overlap in the masks.
   397       isPluginFile = !!(supportedArchitectures & pluginLibArchitectures);
   398     }
   399     ::CFRelease(pluginBundle);
   400   }
   402   ::CFRelease(pluginURL);
   403   return isPluginFile;
   404 }
   406 /**
   407  * Obtains all of the information currently available for this plugin.
   408  */
   409 nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary)
   410 {
   411   *outLibrary = nullptr;
   413   nsresult rv = NS_OK;
   415   if (!IsCompatibleArch(mPlugin)) {
   416       return NS_ERROR_FAILURE;
   417   }
   419   // clear out the info, except for the first field.
   420   memset(&info, 0, sizeof(info));
   422   // Try to get a bundle reference.
   423   nsAutoCString path;
   424   if (NS_FAILED(rv = mPlugin->GetNativePath(path)))
   425     return rv;
   426   CFBundleRef bundle = getPluginBundle(path.get());
   428   // fill in full path
   429   info.fFullPath = PL_strdup(path.get());
   431   // fill in file name
   432   nsAutoCString fileName;
   433   if (NS_FAILED(rv = mPlugin->GetNativeLeafName(fileName)))
   434     return rv;
   435   info.fFileName = PL_strdup(fileName.get());
   437   // Get fName
   438   if (bundle) {
   439     CFTypeRef name = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginName"));
   440     if (name && ::CFGetTypeID(name) == ::CFStringGetTypeID())
   441       info.fName = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(name));
   442   }
   444   // Get fDescription
   445   if (bundle) {
   446     CFTypeRef description = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginDescription"));
   447     if (description && ::CFGetTypeID(description) == ::CFStringGetTypeID())
   448       info.fDescription = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(description));
   449   }
   451   // Get fVersion
   452   if (bundle) {
   453     // Look for the release version first
   454     CFTypeRef version = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString"));
   455     if (!version) // try the build version
   456       version = ::CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey);
   457     if (version && ::CFGetTypeID(version) == ::CFStringGetTypeID())
   458       info.fVersion = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(version));
   459   }
   461   // The last thing we need to do is get MIME data
   462   // fVariantCount, fMimeTypeArray, fExtensionArray, fMimeDescriptionArray
   464   // First look for data in a bundle plist
   465   if (bundle) {
   466     ParsePlistPluginInfo(info, bundle);
   467     ::CFRelease(bundle);
   468     if (info.fVariantCount > 0)
   469       return NS_OK;
   470   }
   472   // It's possible that our plugin has 2 entry points that'll give us mime type
   473   // info. Quicktime does this to get around the need of having admin rights to
   474   // change mime info in the resource fork. We need to use this info instead of
   475   // the resource. See bug 113464.
   477   // Sadly we have to load the library for this to work.
   478   rv = LoadPlugin(outLibrary);
   479   if (NS_FAILED(rv))
   480     return rv;
   482   // Try to get data from NP_GetMIMEDescription
   483   if (pLibrary) {
   484     NP_GETMIMEDESCRIPTION pfnGetMimeDesc = (NP_GETMIMEDESCRIPTION)PR_FindFunctionSymbol(pLibrary, NP_GETMIMEDESCRIPTION_NAME); 
   485     if (pfnGetMimeDesc)
   486       ParsePluginMimeDescription(pfnGetMimeDesc(), info);
   487     if (info.fVariantCount)
   488       return NS_OK;
   489   }
   491   // We'll fill this in using BP_GetSupportedMIMETypes and/or resource fork data
   492   BPSupportedMIMETypes mi = {kBPSupportedMIMETypesStructVers_1, nullptr, nullptr};
   494   // Try to get data from BP_GetSupportedMIMETypes
   495   if (pLibrary) {
   496     BP_GETSUPPORTEDMIMETYPES pfnMime = (BP_GETSUPPORTEDMIMETYPES)PR_FindFunctionSymbol(pLibrary, "BP_GetSupportedMIMETypes");
   497     if (pfnMime && noErr == pfnMime(&mi, 0) && mi.typeStrings) {
   498       info.fVariantCount = (**(short**)mi.typeStrings) / 2;
   499       ::HLock(mi.typeStrings);
   500       if (mi.infoStrings)  // it's possible some plugins have infoStrings missing
   501         ::HLock(mi.infoStrings);
   502     }
   503   }
   505   // Fill in the info struct based on the data in the BPSupportedMIMETypes struct
   506   int variantCount = info.fVariantCount;
   507   info.fMimeTypeArray = static_cast<char**>(NS_Alloc(variantCount * sizeof(char*)));
   508   if (!info.fMimeTypeArray)
   509     return NS_ERROR_OUT_OF_MEMORY;
   510   info.fExtensionArray = static_cast<char**>(NS_Alloc(variantCount * sizeof(char*)));
   511   if (!info.fExtensionArray)
   512     return NS_ERROR_OUT_OF_MEMORY;
   513   if (mi.infoStrings) {
   514     info.fMimeDescriptionArray = static_cast<char**>(NS_Alloc(variantCount * sizeof(char*)));
   515     if (!info.fMimeDescriptionArray)
   516       return NS_ERROR_OUT_OF_MEMORY;
   517   }
   518   short mimeIndex = 2;
   519   short descriptionIndex = 2;
   520   for (int i = 0; i < variantCount; i++) {
   521     info.fMimeTypeArray[i] = GetNextPluginStringFromHandle(mi.typeStrings, &mimeIndex);
   522     info.fExtensionArray[i] = GetNextPluginStringFromHandle(mi.typeStrings, &mimeIndex);
   523     if (mi.infoStrings)
   524       info.fMimeDescriptionArray[i] = GetNextPluginStringFromHandle(mi.infoStrings, &descriptionIndex);
   525   }
   527   ::HUnlock(mi.typeStrings);
   528   ::DisposeHandle(mi.typeStrings);
   529   if (mi.infoStrings) {
   530     ::HUnlock(mi.infoStrings);
   531     ::DisposeHandle(mi.infoStrings);
   532   }
   534   return NS_OK;
   535 }
   537 nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info)
   538 {
   539   NS_Free(info.fName);
   540   NS_Free(info.fDescription);
   541   int variantCount = info.fVariantCount;
   542   for (int i = 0; i < variantCount; i++) {
   543     NS_Free(info.fMimeTypeArray[i]);
   544     NS_Free(info.fExtensionArray[i]);
   545     NS_Free(info.fMimeDescriptionArray[i]);
   546   }
   547   NS_Free(info.fMimeTypeArray);
   548   NS_Free(info.fMimeDescriptionArray);
   549   NS_Free(info.fExtensionArray);
   550   NS_Free(info.fFileName);
   551   NS_Free(info.fFullPath);
   552   NS_Free(info.fVersion);
   554   return NS_OK;
   555 }

mercurial