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