commit e0841547af0b8efc3e3191b9a249e105623bfaab
parent 2c11ffaf4707c8efc3de8b7ea8a2b8471c50d69d
Author: Josh Aas <jaas@kflag.net>
Date: Wed, 5 Nov 2025 13:52:04 +0000
Bug 1994081 - Fix issue in which Firefox on macOS does not reliably get helper application names. r=spohl
Differential Revision: https://phabricator.services.mozilla.com/D271342
Diffstat:
1 file changed, 57 insertions(+), 39 deletions(-)
diff --git a/uriloader/exthandler/mac/nsOSHelperAppService.mm b/uriloader/exthandler/mac/nsOSHelperAppService.mm
@@ -93,6 +93,8 @@ nsOSHelperAppService::~nsOSHelperAppService() {}
nsresult nsOSHelperAppService::OSProtocolHandlerExists(
const char* aProtocolScheme, bool* aHandlerExists) {
+ *aHandlerExists = false;
+
// CFStringCreateWithBytes() can fail even if we're not out of memory --
// for example if the 'bytes' parameter is something very weird (like
// "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
@@ -100,24 +102,27 @@ nsresult nsOSHelperAppService::OSProtocolHandlerExists(
CFStringRef schemeString = ::CFStringCreateWithBytes(
kCFAllocatorDefault, (const UInt8*)aProtocolScheme,
strlen(aProtocolScheme), kCFStringEncodingUTF8, false);
- if (schemeString) {
- // LSCopyDefaultHandlerForURLScheme() can fail to find the default handler
- // for aProtocolScheme when it's never been explicitly set (using
- // LSSetDefaultHandlerForURLScheme()). For example, Safari is the default
- // handler for the "http" scheme on a newly installed copy of OS X. But
- // this (presumably) wasn't done using LSSetDefaultHandlerForURLScheme(),
- // so LSCopyDefaultHandlerForURLScheme() will fail to find Safari. To get
- // around this we use LSCopyAllHandlersForURLScheme() instead -- which seems
- // never to fail.
- // http://lists.apple.com/archives/Carbon-dev/2007/May/msg00349.html
- // http://www.realsoftware.com/listarchives/realbasic-nug/2008-02/msg00119.html
- CFArrayRef handlerArray = ::LSCopyAllHandlersForURLScheme(schemeString);
- *aHandlerExists = !!handlerArray;
- if (handlerArray) ::CFRelease(handlerArray);
- ::CFRelease(schemeString);
- } else {
- *aHandlerExists = false;
+ if (!schemeString) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // LSCopyDefaultHandlerForURLScheme() can fail to find the default handler
+ // for aProtocolScheme when it's never been explicitly set (using
+ // LSSetDefaultHandlerForURLScheme()). For example, Safari is the default
+ // handler for the "http" scheme on a newly installed copy of OS X. But
+ // this (presumably) wasn't done using LSSetDefaultHandlerForURLScheme(),
+ // so LSCopyDefaultHandlerForURLScheme() will fail to find Safari. To get
+ // around this we use LSCopyAllHandlersForURLScheme() instead -- which seems
+ // never to fail.
+ // http://lists.apple.com/archives/Carbon-dev/2007/May/msg00349.html
+ // http://www.realsoftware.com/listarchives/realbasic-nug/2008-02/msg00119.html
+ CFArrayRef handlerArray = ::LSCopyAllHandlersForURLScheme(schemeString);
+ ::CFRelease(schemeString);
+ if (handlerArray) {
+ *aHandlerExists = true;
+ ::CFRelease(handlerArray);
}
+
return NS_OK;
}
@@ -125,38 +130,51 @@ NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(
const nsACString& aScheme, nsAString& _retval) {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
- nsresult rv = NS_ERROR_NOT_AVAILABLE;
-
CFURLRef handlerBundleURL;
- rv = GetDefaultBundleURL(aScheme, &handlerBundleURL);
-
- if (NS_SUCCEEDED(rv) && handlerBundleURL) {
- CFBundleRef handlerBundle = CFBundleCreate(NULL, handlerBundleURL);
- if (!handlerBundle) {
+ nsresult rv = GetDefaultBundleURL(aScheme, &handlerBundleURL);
+ if (NS_FAILED(rv)) {
+ if (handlerBundleURL) {
::CFRelease(handlerBundleURL);
- return NS_ERROR_OUT_OF_MEMORY;
}
+ return rv;
+ }
+
+ // Default to just using the application's name.
+ CFStringRef bundleName = ::CFURLCopyLastPathComponent(handlerBundleURL);
+ if (!bundleName) {
+ return NS_ERROR_FAILURE;
+ }
- // Get the human-readable name of the bundle
- CFStringRef bundleName =
+ // See if we can get the bundle display name from the plist.
+ CFBundleRef handlerBundle =
+ ::CFBundleCreate(kCFAllocatorDefault, handlerBundleURL);
+ ::CFRelease(handlerBundleURL);
+ if (handlerBundle) {
+ // Memory management for the value returned here is tricky. We don't have
+ // to release this value, but we can't retain it and we can't release the
+ // underlying bundle until we're done with this value. Just make a copy so
+ // we can ignore this and just assume release for all bundleName values.
+ CFStringRef tmpBundleName =
(CFStringRef)::CFBundleGetValueForInfoDictionaryKey(handlerBundle,
kCFBundleNameKey);
-
- if (bundleName) {
- AutoTArray<UniChar, 255> buffer;
- CFIndex bundleNameLength = ::CFStringGetLength(bundleName);
- buffer.SetLength(bundleNameLength);
- ::CFStringGetCharacters(bundleName, CFRangeMake(0, bundleNameLength),
- buffer.Elements());
- _retval.Assign(reinterpret_cast<char16_t*>(buffer.Elements()),
- bundleNameLength);
- rv = NS_OK;
+ if (tmpBundleName && (::CFStringGetLength(tmpBundleName) > 0)) {
+ ::CFRelease(bundleName);
+ bundleName = ::CFStringCreateCopy(kCFAllocatorDefault, tmpBundleName);
}
::CFRelease(handlerBundle);
- ::CFRelease(handlerBundleURL);
}
- return rv;
+ AutoTArray<UniChar, 255> buffer;
+ CFIndex bundleNameLength = ::CFStringGetLength(bundleName);
+ buffer.SetLength(bundleNameLength);
+ ::CFStringGetCharacters(bundleName, CFRangeMake(0, bundleNameLength),
+ buffer.Elements());
+ _retval.Assign(reinterpret_cast<char16_t*>(buffer.Elements()),
+ bundleNameLength);
+
+ ::CFRelease(bundleName);
+
+ return NS_OK;
NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}