uriloader/exthandler/mac/nsOSHelperAppService.mm

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 *
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include <sys/types.h>
michael@0 8 #include <sys/stat.h>
michael@0 9 #include "nsOSHelperAppService.h"
michael@0 10 #include "nsObjCExceptions.h"
michael@0 11 #include "nsISupports.h"
michael@0 12 #include "nsString.h"
michael@0 13 #include "nsTArray.h"
michael@0 14 #include "nsXPIDLString.h"
michael@0 15 #include "nsIURL.h"
michael@0 16 #include "nsIFile.h"
michael@0 17 #include "nsILocalFileMac.h"
michael@0 18 #include "nsMimeTypes.h"
michael@0 19 #include "nsIStringBundle.h"
michael@0 20 #include "nsIPromptService.h"
michael@0 21 #include "nsMemory.h"
michael@0 22 #include "nsCRT.h"
michael@0 23 #include "nsMIMEInfoMac.h"
michael@0 24 #include "nsEmbedCID.h"
michael@0 25
michael@0 26 #import <CoreFoundation/CoreFoundation.h>
michael@0 27 #import <ApplicationServices/ApplicationServices.h>
michael@0 28
michael@0 29 // chrome URL's
michael@0 30 #define HELPERAPPLAUNCHER_BUNDLE_URL "chrome://global/locale/helperAppLauncher.properties"
michael@0 31 #define BRAND_BUNDLE_URL "chrome://branding/locale/brand.properties"
michael@0 32
michael@0 33 /* This is an undocumented interface (in the Foundation framework) that has
michael@0 34 * been stable since at least 10.2.8 and is still present on SnowLeopard.
michael@0 35 * Furthermore WebKit has three public methods (in WebKitSystemInterface.h)
michael@0 36 * that are thin wrappers around this interface's last three methods. So
michael@0 37 * it's unlikely to change anytime soon. Now that we're no longer using
michael@0 38 * Internet Config Services, this is the only way to look up a MIME type
michael@0 39 * from an extension, or vice versa.
michael@0 40 */
michael@0 41 @class NSURLFileTypeMappingsInternal;
michael@0 42
michael@0 43 @interface NSURLFileTypeMappings : NSObject
michael@0 44 {
michael@0 45 NSURLFileTypeMappingsInternal *_internal;
michael@0 46 }
michael@0 47
michael@0 48 + (NSURLFileTypeMappings*)sharedMappings;
michael@0 49 - (NSString*)MIMETypeForExtension:(NSString*)aString;
michael@0 50 - (NSString*)preferredExtensionForMIMEType:(NSString*)aString;
michael@0 51 - (NSArray*)extensionsForMIMEType:(NSString*)aString;
michael@0 52 @end
michael@0 53
michael@0 54 nsOSHelperAppService::nsOSHelperAppService() : nsExternalHelperAppService()
michael@0 55 {
michael@0 56 mode_t mask = umask(0777);
michael@0 57 umask(mask);
michael@0 58 mPermissions = 0666 & ~mask;
michael@0 59 }
michael@0 60
michael@0 61 nsOSHelperAppService::~nsOSHelperAppService()
michael@0 62 {}
michael@0 63
michael@0 64 nsresult nsOSHelperAppService::OSProtocolHandlerExists(const char * aProtocolScheme, bool * aHandlerExists)
michael@0 65 {
michael@0 66 // CFStringCreateWithBytes() can fail even if we're not out of memory --
michael@0 67 // for example if the 'bytes' parameter is something very wierd (like "ÿÿ~"
michael@0 68 // aka "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
michael@0 69 // specified in the 'encoding' parameter. See bug 548719.
michael@0 70 CFStringRef schemeString = ::CFStringCreateWithBytes(kCFAllocatorDefault,
michael@0 71 (const UInt8*)aProtocolScheme,
michael@0 72 strlen(aProtocolScheme),
michael@0 73 kCFStringEncodingUTF8,
michael@0 74 false);
michael@0 75 if (schemeString) {
michael@0 76 // LSCopyDefaultHandlerForURLScheme() can fail to find the default handler
michael@0 77 // for aProtocolScheme when it's never been explicitly set (using
michael@0 78 // LSSetDefaultHandlerForURLScheme()). For example, Safari is the default
michael@0 79 // handler for the "http" scheme on a newly installed copy of OS X. But
michael@0 80 // this (presumably) wasn't done using LSSetDefaultHandlerForURLScheme(),
michael@0 81 // so LSCopyDefaultHandlerForURLScheme() will fail to find Safari. To get
michael@0 82 // around this we use LSCopyAllHandlersForURLScheme() instead -- which seems
michael@0 83 // never to fail.
michael@0 84 // http://lists.apple.com/archives/Carbon-dev/2007/May/msg00349.html
michael@0 85 // http://www.realsoftware.com/listarchives/realbasic-nug/2008-02/msg00119.html
michael@0 86 CFArrayRef handlerArray = ::LSCopyAllHandlersForURLScheme(schemeString);
michael@0 87 *aHandlerExists = !!handlerArray;
michael@0 88 if (handlerArray)
michael@0 89 ::CFRelease(handlerArray);
michael@0 90 ::CFRelease(schemeString);
michael@0 91 } else {
michael@0 92 *aHandlerExists = false;
michael@0 93 }
michael@0 94 return NS_OK;
michael@0 95 }
michael@0 96
michael@0 97 NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
michael@0 98 {
michael@0 99 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
michael@0 100
michael@0 101 nsresult rv = NS_ERROR_NOT_AVAILABLE;
michael@0 102
michael@0 103 CFStringRef schemeCFString =
michael@0 104 ::CFStringCreateWithBytes(kCFAllocatorDefault,
michael@0 105 (const UInt8 *)PromiseFlatCString(aScheme).get(),
michael@0 106 aScheme.Length(),
michael@0 107 kCFStringEncodingUTF8,
michael@0 108 false);
michael@0 109
michael@0 110 if (schemeCFString) {
michael@0 111 CFStringRef lookupCFString = ::CFStringCreateWithFormat(NULL, NULL, CFSTR("%@:"), schemeCFString);
michael@0 112
michael@0 113 if (lookupCFString) {
michael@0 114 CFURLRef lookupCFURL = ::CFURLCreateWithString(NULL, lookupCFString, NULL);
michael@0 115
michael@0 116 if (lookupCFURL) {
michael@0 117 CFURLRef appCFURL = NULL;
michael@0 118 OSStatus theErr = ::LSGetApplicationForURL(lookupCFURL, kLSRolesAll, NULL, &appCFURL);
michael@0 119
michael@0 120 if (theErr == noErr) {
michael@0 121 CFBundleRef handlerBundle = ::CFBundleCreate(NULL, appCFURL);
michael@0 122
michael@0 123 if (handlerBundle) {
michael@0 124 // Get the human-readable name of the default handler bundle
michael@0 125 CFStringRef bundleName =
michael@0 126 (CFStringRef)::CFBundleGetValueForInfoDictionaryKey(handlerBundle,
michael@0 127 kCFBundleNameKey);
michael@0 128
michael@0 129 if (bundleName) {
michael@0 130 nsAutoTArray<UniChar, 255> buffer;
michael@0 131 CFIndex bundleNameLength = ::CFStringGetLength(bundleName);
michael@0 132 buffer.SetLength(bundleNameLength);
michael@0 133 ::CFStringGetCharacters(bundleName, CFRangeMake(0, bundleNameLength),
michael@0 134 buffer.Elements());
michael@0 135 _retval.Assign(reinterpret_cast<char16_t*>(buffer.Elements()), bundleNameLength);
michael@0 136 rv = NS_OK;
michael@0 137 }
michael@0 138
michael@0 139 ::CFRelease(handlerBundle);
michael@0 140 }
michael@0 141
michael@0 142 ::CFRelease(appCFURL);
michael@0 143 }
michael@0 144
michael@0 145 ::CFRelease(lookupCFURL);
michael@0 146 }
michael@0 147
michael@0 148 ::CFRelease(lookupCFString);
michael@0 149 }
michael@0 150
michael@0 151 ::CFRelease(schemeCFString);
michael@0 152 }
michael@0 153
michael@0 154 return rv;
michael@0 155
michael@0 156 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
michael@0 157 }
michael@0 158
michael@0 159 nsresult nsOSHelperAppService::GetFileTokenForPath(const char16_t * aPlatformAppPath, nsIFile ** aFile)
michael@0 160 {
michael@0 161 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
michael@0 162
michael@0 163 nsresult rv;
michael@0 164 nsCOMPtr<nsILocalFileMac> localFile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
michael@0 165 NS_ENSURE_SUCCESS(rv,rv);
michael@0 166
michael@0 167 CFURLRef pathAsCFURL;
michael@0 168 CFStringRef pathAsCFString = ::CFStringCreateWithCharacters(NULL,
michael@0 169 reinterpret_cast<const UniChar*>(aPlatformAppPath),
michael@0 170 NS_strlen(aPlatformAppPath));
michael@0 171 if (!pathAsCFString)
michael@0 172 return NS_ERROR_OUT_OF_MEMORY;
michael@0 173
michael@0 174 if (::CFStringGetCharacterAtIndex(pathAsCFString, 0) == '/') {
michael@0 175 // we have a Posix path
michael@0 176 pathAsCFURL = ::CFURLCreateWithFileSystemPath(nullptr, pathAsCFString,
michael@0 177 kCFURLPOSIXPathStyle, false);
michael@0 178 if (!pathAsCFURL) {
michael@0 179 ::CFRelease(pathAsCFString);
michael@0 180 return NS_ERROR_OUT_OF_MEMORY;
michael@0 181 }
michael@0 182 }
michael@0 183 else {
michael@0 184 // if it doesn't start with a / it's not an absolute Posix path
michael@0 185 // let's check if it's a HFS path left over from old preferences
michael@0 186
michael@0 187 // If it starts with a ':' char, it's not an absolute HFS path
michael@0 188 // so bail for that, and also if it's empty
michael@0 189 if (::CFStringGetLength(pathAsCFString) == 0 ||
michael@0 190 ::CFStringGetCharacterAtIndex(pathAsCFString, 0) == ':')
michael@0 191 {
michael@0 192 ::CFRelease(pathAsCFString);
michael@0 193 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
michael@0 194 }
michael@0 195
michael@0 196 pathAsCFURL = ::CFURLCreateWithFileSystemPath(nullptr, pathAsCFString,
michael@0 197 kCFURLHFSPathStyle, false);
michael@0 198 if (!pathAsCFURL) {
michael@0 199 ::CFRelease(pathAsCFString);
michael@0 200 return NS_ERROR_OUT_OF_MEMORY;
michael@0 201 }
michael@0 202 }
michael@0 203
michael@0 204 rv = localFile->InitWithCFURL(pathAsCFURL);
michael@0 205 ::CFRelease(pathAsCFString);
michael@0 206 ::CFRelease(pathAsCFURL);
michael@0 207 if (NS_FAILED(rv))
michael@0 208 return rv;
michael@0 209 *aFile = localFile;
michael@0 210 NS_IF_ADDREF(*aFile);
michael@0 211
michael@0 212 return NS_OK;
michael@0 213
michael@0 214 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
michael@0 215 }
michael@0 216
michael@0 217 NS_IMETHODIMP nsOSHelperAppService::GetFromTypeAndExtension(const nsACString& aType, const nsACString& aFileExt, nsIMIMEInfo ** aMIMEInfo)
michael@0 218 {
michael@0 219 return nsExternalHelperAppService::GetFromTypeAndExtension(aType, aFileExt, aMIMEInfo);
michael@0 220 }
michael@0 221
michael@0 222 // Returns the MIME types an application bundle explicitly claims to handle.
michael@0 223 // Returns NULL if aAppRef doesn't explicitly claim to handle any MIME types.
michael@0 224 // If the return value is non-NULL, the caller is responsible for freeing it.
michael@0 225 // This isn't necessarily the same as the MIME types the application bundle
michael@0 226 // is registered to handle in the Launch Services database. (For example
michael@0 227 // the Preview application is normally registered to handle the application/pdf
michael@0 228 // MIME type, even though it doesn't explicitly claim to handle *any* MIME
michael@0 229 // types in its Info.plist. This is probably because Preview does explicitly
michael@0 230 // claim to handle the com.adobe.pdf UTI, and Launch Services somehow
michael@0 231 // translates this into a claim to support the application/pdf MIME type.
michael@0 232 // Launch Services doesn't provide any APIs (documented or undocumented) to
michael@0 233 // query which MIME types a given application is registered to handle. So any
michael@0 234 // app that wants this information (e.g. the Default Apps pref pane) needs to
michael@0 235 // iterate through the entire Launch Services database -- a process which can
michael@0 236 // take several seconds.)
michael@0 237 static CFArrayRef GetMIMETypesHandledByApp(FSRef *aAppRef)
michael@0 238 {
michael@0 239 CFURLRef appURL = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aAppRef);
michael@0 240 if (!appURL) {
michael@0 241 return NULL;
michael@0 242 }
michael@0 243 CFDictionaryRef infoDict = ::CFBundleCopyInfoDictionaryForURL(appURL);
michael@0 244 ::CFRelease(appURL);
michael@0 245 if (!infoDict) {
michael@0 246 return NULL;
michael@0 247 }
michael@0 248 CFTypeRef cfObject = ::CFDictionaryGetValue(infoDict, CFSTR("CFBundleDocumentTypes"));
michael@0 249 if (!cfObject || (::CFGetTypeID(cfObject) != ::CFArrayGetTypeID())) {
michael@0 250 ::CFRelease(infoDict);
michael@0 251 return NULL;
michael@0 252 }
michael@0 253
michael@0 254 CFArrayRef docTypes = static_cast<CFArrayRef>(cfObject);
michael@0 255 CFIndex docTypesCount = ::CFArrayGetCount(docTypes);
michael@0 256 if (docTypesCount == 0) {
michael@0 257 ::CFRelease(infoDict);
michael@0 258 return NULL;
michael@0 259 }
michael@0 260
michael@0 261 CFMutableArrayRef mimeTypes =
michael@0 262 ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
michael@0 263 for (CFIndex i = 0; i < docTypesCount; ++i) {
michael@0 264 cfObject = ::CFArrayGetValueAtIndex(docTypes, i);
michael@0 265 if (!cfObject || (::CFGetTypeID(cfObject) != ::CFDictionaryGetTypeID())) {
michael@0 266 continue;
michael@0 267 }
michael@0 268 CFDictionaryRef typeDict = static_cast<CFDictionaryRef>(cfObject);
michael@0 269
michael@0 270 // When this key is present (on OS X 10.5 and later), its contents
michael@0 271 // take precedence over CFBundleTypeMIMETypes (and CFBundleTypeExtensions
michael@0 272 // and CFBundleTypeOSTypes).
michael@0 273 cfObject = ::CFDictionaryGetValue(typeDict, CFSTR("LSItemContentTypes"));
michael@0 274 if (cfObject && (::CFGetTypeID(cfObject) == ::CFArrayGetTypeID())) {
michael@0 275 continue;
michael@0 276 }
michael@0 277
michael@0 278 cfObject = ::CFDictionaryGetValue(typeDict, CFSTR("CFBundleTypeMIMETypes"));
michael@0 279 if (!cfObject || (::CFGetTypeID(cfObject) != ::CFArrayGetTypeID())) {
michael@0 280 continue;
michael@0 281 }
michael@0 282 CFArrayRef mimeTypeHolder = static_cast<CFArrayRef>(cfObject);
michael@0 283 CFArrayAppendArray(mimeTypes, mimeTypeHolder,
michael@0 284 ::CFRangeMake(0, ::CFArrayGetCount(mimeTypeHolder)));
michael@0 285 }
michael@0 286
michael@0 287 ::CFRelease(infoDict);
michael@0 288 if (!::CFArrayGetCount(mimeTypes)) {
michael@0 289 ::CFRelease(mimeTypes);
michael@0 290 mimeTypes = NULL;
michael@0 291 }
michael@0 292 return mimeTypes;
michael@0 293 }
michael@0 294
michael@0 295 // aMIMEType and aFileExt might not match, If they don't we set *aFound to
michael@0 296 // false and return a minimal nsIMIMEInfo structure.
michael@0 297 already_AddRefed<nsIMIMEInfo>
michael@0 298 nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType,
michael@0 299 const nsACString& aFileExt,
michael@0 300 bool * aFound)
michael@0 301 {
michael@0 302 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
michael@0 303
michael@0 304 *aFound = false;
michael@0 305
michael@0 306 const nsCString& flatType = PromiseFlatCString(aMIMEType);
michael@0 307 const nsCString& flatExt = PromiseFlatCString(aFileExt);
michael@0 308
michael@0 309 PR_LOG(mLog, PR_LOG_DEBUG, ("Mac: HelperAppService lookup for type '%s' ext '%s'\n",
michael@0 310 flatType.get(), flatExt.get()));
michael@0 311
michael@0 312 // Create a Mac-specific MIME info so we can use Mac-specific members.
michael@0 313 nsRefPtr<nsMIMEInfoMac> mimeInfoMac = new nsMIMEInfoMac(aMIMEType);
michael@0 314
michael@0 315 NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
michael@0 316
michael@0 317 OSStatus err;
michael@0 318 bool haveAppForType = false;
michael@0 319 bool haveAppForExt = false;
michael@0 320 bool typeAppIsDefault = false;
michael@0 321 bool extAppIsDefault = false;
michael@0 322 FSRef typeAppFSRef;
michael@0 323 FSRef extAppFSRef;
michael@0 324
michael@0 325 CFStringRef cfMIMEType = NULL;
michael@0 326
michael@0 327 if (!aMIMEType.IsEmpty()) {
michael@0 328 CFURLRef appURL = NULL;
michael@0 329 // CFStringCreateWithCString() can fail even if we're not out of memory --
michael@0 330 // for example if the 'cStr' parameter is something very wierd (like "ÿÿ~"
michael@0 331 // aka "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
michael@0 332 // specified in the 'encoding' parameter. See bug 548719.
michael@0 333 cfMIMEType = ::CFStringCreateWithCString(NULL, flatType.get(),
michael@0 334 kCFStringEncodingUTF8);
michael@0 335 if (cfMIMEType) {
michael@0 336 err = ::LSCopyApplicationForMIMEType(cfMIMEType, kLSRolesAll, &appURL);
michael@0 337 if ((err == noErr) && appURL && ::CFURLGetFSRef(appURL, &typeAppFSRef)) {
michael@0 338 haveAppForType = true;
michael@0 339 PR_LOG(mLog, PR_LOG_DEBUG, ("LSCopyApplicationForMIMEType found a default application\n"));
michael@0 340 }
michael@0 341 if (appURL) {
michael@0 342 ::CFRelease(appURL);
michael@0 343 }
michael@0 344 }
michael@0 345 }
michael@0 346 if (!aFileExt.IsEmpty()) {
michael@0 347 // CFStringCreateWithCString() can fail even if we're not out of memory --
michael@0 348 // for example if the 'cStr' parameter is something very wierd (like "ÿÿ~"
michael@0 349 // aka "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
michael@0 350 // specified in the 'encoding' parameter. See bug 548719.
michael@0 351 CFStringRef cfExt = ::CFStringCreateWithCString(NULL, flatExt.get(), kCFStringEncodingUTF8);
michael@0 352 if (cfExt) {
michael@0 353 err = ::LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, cfExt,
michael@0 354 kLSRolesAll, &extAppFSRef, nullptr);
michael@0 355 if (err == noErr) {
michael@0 356 haveAppForExt = true;
michael@0 357 PR_LOG(mLog, PR_LOG_DEBUG, ("LSGetApplicationForInfo found a default application\n"));
michael@0 358 }
michael@0 359 ::CFRelease(cfExt);
michael@0 360 }
michael@0 361 }
michael@0 362
michael@0 363 if (haveAppForType && haveAppForExt) {
michael@0 364 // Do aMIMEType and aFileExt match?
michael@0 365 if (::FSCompareFSRefs((const FSRef *) &typeAppFSRef, (const FSRef *) &extAppFSRef) == noErr) {
michael@0 366 typeAppIsDefault = true;
michael@0 367 *aFound = true;
michael@0 368 }
michael@0 369 } else if (haveAppForType) {
michael@0 370 // If aFileExt isn't empty, it doesn't match aMIMEType.
michael@0 371 if (aFileExt.IsEmpty()) {
michael@0 372 typeAppIsDefault = true;
michael@0 373 *aFound = true;
michael@0 374 }
michael@0 375 } else if (haveAppForExt) {
michael@0 376 // If aMIMEType isn't empty, it doesn't match aFileExt, which should mean
michael@0 377 // that we haven't found a matching app. But make an exception for an app
michael@0 378 // that also explicitly claims to handle aMIMEType, or which doesn't claim
michael@0 379 // to handle any MIME types. This helps work around the following Apple
michael@0 380 // design flaw:
michael@0 381 //
michael@0 382 // Launch Services is somewhat unreliable about registering Apple apps to
michael@0 383 // handle MIME types. Probably this is because Apple has officially
michael@0 384 // deprecated support for MIME types (in favor of UTIs). As a result,
michael@0 385 // most of Apple's own apps don't explicitly claim to handle any MIME
michael@0 386 // types (instead they claim to handle one or more UTIs). So Launch
michael@0 387 // Services must contain logic to translate support for a given UTI into
michael@0 388 // support for one or more MIME types, and it doesn't always do this
michael@0 389 // correctly. For example DiskImageMounter isn't (by default) registered
michael@0 390 // to handle the application/x-apple-diskimage MIME type. See bug 675356.
michael@0 391 //
michael@0 392 // Apple has also deprecated support for file extensions, and Apple apps
michael@0 393 // also don't register to handle them. But for some reason Launch Services
michael@0 394 // is (apparently) better about translating support for a given UTI into
michael@0 395 // support for one or more file extensions. It's not at all clear why.
michael@0 396 if (aMIMEType.IsEmpty()) {
michael@0 397 extAppIsDefault = true;
michael@0 398 *aFound = true;
michael@0 399 } else {
michael@0 400 CFArrayRef extAppMIMETypes = GetMIMETypesHandledByApp(&extAppFSRef);
michael@0 401 if (extAppMIMETypes) {
michael@0 402 if (cfMIMEType) {
michael@0 403 if (::CFArrayContainsValue(extAppMIMETypes,
michael@0 404 ::CFRangeMake(0, ::CFArrayGetCount(extAppMIMETypes)),
michael@0 405 cfMIMEType)) {
michael@0 406 extAppIsDefault = true;
michael@0 407 *aFound = true;
michael@0 408 }
michael@0 409 }
michael@0 410 ::CFRelease(extAppMIMETypes);
michael@0 411 } else {
michael@0 412 extAppIsDefault = true;
michael@0 413 *aFound = true;
michael@0 414 }
michael@0 415 }
michael@0 416 }
michael@0 417
michael@0 418 if (cfMIMEType) {
michael@0 419 ::CFRelease(cfMIMEType);
michael@0 420 }
michael@0 421
michael@0 422 if (aMIMEType.IsEmpty()) {
michael@0 423 if (haveAppForExt) {
michael@0 424 // If aMIMEType is empty and we've found a default app for aFileExt, try
michael@0 425 // to get the MIME type from aFileExt. (It might also be worth doing
michael@0 426 // this when aMIMEType isn't empty but haveAppForType is false -- but
michael@0 427 // the doc for this method says that if we have a MIME type (in
michael@0 428 // aMIMEType), we need to give it preference.)
michael@0 429 NSURLFileTypeMappings *map = [NSURLFileTypeMappings sharedMappings];
michael@0 430 NSString *extStr = [NSString stringWithCString:flatExt.get() encoding:NSASCIIStringEncoding];
michael@0 431 NSString *typeStr = map ? [map MIMETypeForExtension:extStr] : NULL;
michael@0 432 if (typeStr) {
michael@0 433 nsAutoCString mimeType;
michael@0 434 mimeType.Assign((char *)[typeStr cStringUsingEncoding:NSASCIIStringEncoding]);
michael@0 435 mimeInfoMac->SetMIMEType(mimeType);
michael@0 436 haveAppForType = true;
michael@0 437 } else {
michael@0 438 // Sometimes the OS won't give us a MIME type for an extension that's
michael@0 439 // registered with Launch Services and has a default app: For example
michael@0 440 // Real Player registers itself for the "ogg" extension and for the
michael@0 441 // audio/x-ogg and application/x-ogg MIME types, but
michael@0 442 // MIMETypeForExtension returns nil for the "ogg" extension even on
michael@0 443 // systems where Real Player is installed. This is probably an Apple
michael@0 444 // bug. But bad things happen if we return an nsIMIMEInfo structure
michael@0 445 // with an empty MIME type and set *aFound to true. So in this
michael@0 446 // case we need to set it to false here.
michael@0 447 haveAppForExt = false;
michael@0 448 extAppIsDefault = false;
michael@0 449 *aFound = false;
michael@0 450 }
michael@0 451 } else {
michael@0 452 // Otherwise set the MIME type to a reasonable fallback.
michael@0 453 mimeInfoMac->SetMIMEType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM));
michael@0 454 }
michael@0 455 }
michael@0 456
michael@0 457 if (typeAppIsDefault || extAppIsDefault) {
michael@0 458 if (haveAppForExt)
michael@0 459 mimeInfoMac->AppendExtension(aFileExt);
michael@0 460
michael@0 461 nsCOMPtr<nsILocalFileMac> app(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
michael@0 462 if (!app) {
michael@0 463 [localPool release];
michael@0 464 return nullptr;
michael@0 465 }
michael@0 466
michael@0 467 CFStringRef cfAppName = NULL;
michael@0 468 if (typeAppIsDefault) {
michael@0 469 app->InitWithFSRef(&typeAppFSRef);
michael@0 470 ::LSCopyItemAttribute((const FSRef *) &typeAppFSRef, kLSRolesAll,
michael@0 471 kLSItemDisplayName, (CFTypeRef *) &cfAppName);
michael@0 472 } else {
michael@0 473 app->InitWithFSRef(&extAppFSRef);
michael@0 474 ::LSCopyItemAttribute((const FSRef *) &extAppFSRef, kLSRolesAll,
michael@0 475 kLSItemDisplayName, (CFTypeRef *) &cfAppName);
michael@0 476 }
michael@0 477 if (cfAppName) {
michael@0 478 nsAutoTArray<UniChar, 255> buffer;
michael@0 479 CFIndex appNameLength = ::CFStringGetLength(cfAppName);
michael@0 480 buffer.SetLength(appNameLength);
michael@0 481 ::CFStringGetCharacters(cfAppName, CFRangeMake(0, appNameLength),
michael@0 482 buffer.Elements());
michael@0 483 nsAutoString appName;
michael@0 484 appName.Assign(reinterpret_cast<char16_t*>(buffer.Elements()), appNameLength);
michael@0 485 mimeInfoMac->SetDefaultDescription(appName);
michael@0 486 ::CFRelease(cfAppName);
michael@0 487 }
michael@0 488
michael@0 489 mimeInfoMac->SetDefaultApplication(app);
michael@0 490 mimeInfoMac->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
michael@0 491 } else {
michael@0 492 mimeInfoMac->SetPreferredAction(nsIMIMEInfo::saveToDisk);
michael@0 493 }
michael@0 494
michael@0 495 nsAutoCString mimeType;
michael@0 496 mimeInfoMac->GetMIMEType(mimeType);
michael@0 497 if (*aFound && !mimeType.IsEmpty()) {
michael@0 498 // If we have a MIME type, make sure its preferred extension is included
michael@0 499 // in our list.
michael@0 500 NSURLFileTypeMappings *map = [NSURLFileTypeMappings sharedMappings];
michael@0 501 NSString *typeStr = [NSString stringWithCString:mimeType.get() encoding:NSASCIIStringEncoding];
michael@0 502 NSString *extStr = map ? [map preferredExtensionForMIMEType:typeStr] : NULL;
michael@0 503 if (extStr) {
michael@0 504 nsAutoCString preferredExt;
michael@0 505 preferredExt.Assign((char *)[extStr cStringUsingEncoding:NSASCIIStringEncoding]);
michael@0 506 mimeInfoMac->AppendExtension(preferredExt);
michael@0 507 }
michael@0 508
michael@0 509 CFStringRef cfType = ::CFStringCreateWithCString(NULL, mimeType.get(), kCFStringEncodingUTF8);
michael@0 510 if (cfType) {
michael@0 511 CFStringRef cfTypeDesc = NULL;
michael@0 512 if (::LSCopyKindStringForMIMEType(cfType, &cfTypeDesc) == noErr) {
michael@0 513 nsAutoTArray<UniChar, 255> buffer;
michael@0 514 CFIndex typeDescLength = ::CFStringGetLength(cfTypeDesc);
michael@0 515 buffer.SetLength(typeDescLength);
michael@0 516 ::CFStringGetCharacters(cfTypeDesc, CFRangeMake(0, typeDescLength),
michael@0 517 buffer.Elements());
michael@0 518 nsAutoString typeDesc;
michael@0 519 typeDesc.Assign(reinterpret_cast<char16_t*>(buffer.Elements()), typeDescLength);
michael@0 520 mimeInfoMac->SetDescription(typeDesc);
michael@0 521 }
michael@0 522 if (cfTypeDesc) {
michael@0 523 ::CFRelease(cfTypeDesc);
michael@0 524 }
michael@0 525 ::CFRelease(cfType);
michael@0 526 }
michael@0 527 }
michael@0 528
michael@0 529 PR_LOG(mLog, PR_LOG_DEBUG, ("OS gave us: type '%s' found '%i'\n", mimeType.get(), *aFound));
michael@0 530
michael@0 531 [localPool release];
michael@0 532 return mimeInfoMac.forget();
michael@0 533
michael@0 534 NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
michael@0 535 }
michael@0 536
michael@0 537 NS_IMETHODIMP
michael@0 538 nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
michael@0 539 bool *found,
michael@0 540 nsIHandlerInfo **_retval)
michael@0 541 {
michael@0 542 NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");
michael@0 543
michael@0 544 nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(),
michael@0 545 found);
michael@0 546 if (NS_FAILED(rv))
michael@0 547 return rv;
michael@0 548
michael@0 549 nsMIMEInfoMac *handlerInfo =
michael@0 550 new nsMIMEInfoMac(aScheme, nsMIMEInfoBase::eProtocolInfo);
michael@0 551 NS_ENSURE_TRUE(handlerInfo, NS_ERROR_OUT_OF_MEMORY);
michael@0 552 NS_ADDREF(*_retval = handlerInfo);
michael@0 553
michael@0 554 if (!*found) {
michael@0 555 // Code that calls this requires an object regardless if the OS has
michael@0 556 // something for us, so we return the empty object.
michael@0 557 return NS_OK;
michael@0 558 }
michael@0 559
michael@0 560 nsAutoString desc;
michael@0 561 GetApplicationDescription(aScheme, desc);
michael@0 562 handlerInfo->SetDefaultDescription(desc);
michael@0 563
michael@0 564 return NS_OK;
michael@0 565 }
michael@0 566

mercurial