Skip to content

Commit 378b95a

Browse files
committed
[Cocoa] Replace deprecated LSGetApplicationForInfo in Program.findProgram
LSGetApplicationForInfo (deprecated macOS 10.10) and CFURLCreateFromFSRef (deprecated macOS 10.9) are replaced by NSWorkspace APIs: - On macOS 12.0+: UTType.typeWithFilenameExtension: combined with NSWorkspace.urlForApplicationToOpenContentType: for reliable content type-based lookup, including third-party file types like .xlsx - Fallback for older macOS: NSWorkspace.urlForApplicationToOpenURL: with a dummy file URL carrying the extension Adds a test asserting that findProgram(".txt") returns a non-null program with a name on Windows and macOS. The test is skipped on Linux because applications for MIME types may not be registered in Linux test environments.
1 parent 98062b7 commit 378b95a

File tree

7 files changed

+60
-70
lines changed

7 files changed

+60
-70
lines changed

bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/library/os.c

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2025 IBM Corporation and others.
2+
* Copyright (c) 2000, 2026 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -1091,22 +1091,6 @@ JNIEXPORT void JNICALL OS_NATIVE(CFRunLoopObserverInvalidate)
10911091
}
10921092
#endif
10931093

1094-
#ifndef NO_CFURLCreateFromFSRef
1095-
JNIEXPORT jlong JNICALL OS_NATIVE(CFURLCreateFromFSRef)
1096-
(JNIEnv *env, jclass that, jlong arg0, jbyteArray arg1)
1097-
{
1098-
jbyte *lparg1=NULL;
1099-
jlong rc = 0;
1100-
OS_NATIVE_ENTER(env, that, CFURLCreateFromFSRef_FUNC);
1101-
if (arg1) if ((lparg1 = (*env)->GetByteArrayElements(env, arg1, NULL)) == NULL) goto fail;
1102-
rc = (jlong)CFURLCreateFromFSRef((CFAllocatorRef)arg0, (FSRef*)lparg1);
1103-
fail:
1104-
if (arg1 && lparg1) (*env)->ReleaseByteArrayElements(env, arg1, lparg1, 0);
1105-
OS_NATIVE_EXIT(env, that, CFURLCreateFromFSRef_FUNC);
1106-
return rc;
1107-
}
1108-
#endif
1109-
11101094
#ifndef NO_CFURLCreateStringByAddingPercentEscapes
11111095
JNIEXPORT jlong JNICALL OS_NATIVE(CFURLCreateStringByAddingPercentEscapes)
11121096
(JNIEnv *env, jclass that, jlong arg0, jlong arg1, jlong arg2, jlong arg3, jint arg4)
@@ -2106,25 +2090,6 @@ JNIEXPORT jbyte JNICALL OS_NATIVE(LMGetKbdType)
21062090
}
21072091
#endif
21082092

2109-
#ifndef NO_LSGetApplicationForInfo
2110-
JNIEXPORT jlong JNICALL OS_NATIVE(LSGetApplicationForInfo)
2111-
(JNIEnv *env, jclass that, jint arg0, jint arg1, jlong arg2, jint arg3, jbyteArray arg4, jintArray arg5)
2112-
{
2113-
jbyte *lparg4=NULL;
2114-
jint *lparg5=NULL;
2115-
jlong rc = 0;
2116-
OS_NATIVE_ENTER(env, that, LSGetApplicationForInfo_FUNC);
2117-
if (arg4) if ((lparg4 = (*env)->GetByteArrayElements(env, arg4, NULL)) == NULL) goto fail;
2118-
if (arg5) if ((lparg5 = (*env)->GetIntArrayElements(env, arg5, NULL)) == NULL) goto fail;
2119-
rc = (jlong)LSGetApplicationForInfo((OSType)arg0, (OSType)arg1, (CFStringRef)arg2, (LSRolesMask)arg3, (FSRef *)lparg4, (CFURLRef *)lparg5);
2120-
fail:
2121-
if (arg5 && lparg5) (*env)->ReleaseIntArrayElements(env, arg5, lparg5, 0);
2122-
if (arg4 && lparg4) (*env)->ReleaseByteArrayElements(env, arg4, lparg4, 0);
2123-
OS_NATIVE_EXIT(env, that, LSGetApplicationForInfo_FUNC);
2124-
return rc;
2125-
}
2126-
#endif
2127-
21282093
#ifndef NO_LineTo
21292094
JNIEXPORT void JNICALL OS_NATIVE(LineTo)
21302095
(JNIEnv *env, jclass that, jshort arg0, jshort arg1)

bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/library/os_stats.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2025 IBM Corporation and others.
2+
* Copyright (c) 2000, 2026 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -70,7 +70,6 @@ typedef enum {
7070
CFRunLoopGetCurrent_FUNC,
7171
CFRunLoopObserverCreate_FUNC,
7272
CFRunLoopObserverInvalidate_FUNC,
73-
CFURLCreateFromFSRef_FUNC,
7473
CFURLCreateStringByAddingPercentEscapes_FUNC,
7574
CGAffineTransform_1sizeof_FUNC,
7675
CGBitmapContextCreate_FUNC,
@@ -149,7 +148,6 @@ typedef enum {
149148
JSStringCreateWithUTF8CString_FUNC,
150149
JSStringRelease_FUNC,
151150
LMGetKbdType_FUNC,
152-
LSGetApplicationForInfo_FUNC,
153151
LineTo_FUNC,
154152
MoveTo_FUNC,
155153
NSAccessibilityAttributedStringForRangeParameterizedAttribute_FUNC,

bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSWorkspace.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2019 IBM Corporation and others.
2+
* Copyright (c) 2000, 2026 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -45,6 +45,16 @@ public boolean openURL(NSURL url) {
4545
return OS.objc_msgSend_bool(this.id, OS.sel_openURL_, url != null ? url.id : 0);
4646
}
4747

48+
public NSURL urlForApplicationToOpenURL(NSURL url) {
49+
long result = OS.objc_msgSend(this.id, OS.sel_URLForApplicationToOpenURL_, url != null ? url.id : 0);
50+
return result != 0 ? new NSURL(result) : null;
51+
}
52+
53+
public NSURL urlForApplicationToOpenContentType(long contentType) {
54+
long result = OS.objc_msgSend(this.id, OS.sel_URLForApplicationToOpenContentType_, contentType);
55+
return result != 0 ? new NSURL(result) : null;
56+
}
57+
4858
public boolean openURLs(NSArray urls, NSString bundleIdentifier, long options, NSAppleEventDescriptor descriptor, long identifiers) {
4959
return OS.objc_msgSend_bool(this.id, OS.sel_openURLs_withAppBundleIdentifier_options_additionalEventParamDescriptor_launchIdentifiers_, urls != null ? urls.id : 0, bundleIdentifier != null ? bundleIdentifier.id : 0, options, descriptor != null ? descriptor.id : 0, identifiers);
5060
}

bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/OS.java

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2007, 2022 IBM Corporation and others.
2+
* Copyright (c) 2007, 2026 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -65,9 +65,6 @@ public static int VERSION (int major, int minor, int bugfix) {
6565
public static final int kUIModeNormal = 0;
6666
public static final int kUIModeContentHidden = 2;
6767
public static final int kUIModeAllHidden = 3;
68-
public static final int kLSUnknownType = 0;
69-
public static final int kLSUnknownCreator = 0;
70-
public static final int kLSRolesAll = 0xFFFFFFFF;
7168
public static final int kAXUnderlineStyleNone = 0x0;
7269
public static final int kAXUnderlineStyleSingle = 0x1;
7370
public static final int kAXUnderlineStyleThick = 0x2;
@@ -313,15 +310,6 @@ public static boolean isBigSurOrLater () {
313310
public static final native long AcquireRootMenu ();
314311
/** @method flags=dynamic */
315312
public static final native int CancelMenuTracking (long inRootMenu, boolean inImmediate, int inDismissalReason);
316-
/**
317-
* @param inType cast=(OSType)
318-
* @param inCreator cast=(OSType)
319-
* @param inExtension cast=(CFStringRef)
320-
* @param inRoleMask cast=(LSRolesMask)
321-
* @param outAppRef cast=(FSRef *)
322-
* @param outAppURL cast=(CFURLRef *)
323-
*/
324-
public static final native long LSGetApplicationForInfo(int inType, int inCreator,long inExtension, int inRoleMask, byte[] outAppRef, int[] outAppURL);
325313
/** @method flags=dynamic
326314
* @param pmSessionInfo cast=(PMPrintSession)
327315
* @param outPMPrinter cast=(PMPrinter *)
@@ -854,6 +842,8 @@ public static Selector getSelector (long value) {
854842
public static final long sel_TIFFRepresentation = Selector.sel_TIFFRepresentation.value;
855843
public static final long sel_URL = Selector.sel_URL.value;
856844
public static final long sel_URLFromPasteboard_ = Selector.sel_URLFromPasteboard_.value;
845+
public static final long sel_URLForApplicationToOpenURL_ = Selector.sel_URLForApplicationToOpenURL_.value;
846+
public static final long sel_URLForApplicationToOpenContentType_ = Selector.sel_URLForApplicationToOpenContentType_.value;
857847
public static final long sel_URLWithString_ = Selector.sel_URLWithString_.value;
858848
public static final long sel_UTF8String = Selector.sel_UTF8String.value;
859849
public static final long sel_abortEditing = Selector.sel_abortEditing.value;
@@ -2061,6 +2051,7 @@ public static Selector getSelector (long value) {
20612051
public static final long sel_type = Selector.sel_type.value;
20622052
public static final long sel_type_conformsToType_ = Selector.sel_type_conformsToType_.value;
20632053
public static final long sel_typeOfFile_error_ = Selector.sel_typeOfFile_error_.value;
2054+
public static final long sel_typeWithFilenameExtension_ = Selector.sel_typeWithFilenameExtension_.value;
20642055
public static final long sel_types = Selector.sel_types.value;
20652056
public static final long sel_typesetter = Selector.sel_typesetter.value;
20662057
public static final long sel_unarchiveObjectWithData_ = Selector.sel_unarchiveObjectWithData_.value;
@@ -2975,11 +2966,6 @@ public static Selector getSelector (long value) {
29752966
* @param observer cast=(CFRunLoopObserverRef)
29762967
*/
29772968
public static final native void CFRunLoopObserverInvalidate(long observer);
2978-
/**
2979-
* @param allocator cast=(CFAllocatorRef)
2980-
* @param fsRef cast=(FSRef*)
2981-
*/
2982-
public static final native long CFURLCreateFromFSRef(long allocator, byte[] fsRef);
29832969
/**
29842970
* @param allocator cast=(CFAllocatorRef)
29852971
* @param originalString cast=(CFStringRef)

bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/Selector.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2017 itemis AG and others.
2+
* Copyright (c) 2017, 2026 itemis AG and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -97,6 +97,8 @@ public enum Selector {
9797
, sel_TIFFRepresentation("TIFFRepresentation")
9898
, sel_URL("URL")
9999
, sel_URLFromPasteboard_("URLFromPasteboard:")
100+
, sel_URLForApplicationToOpenURL_("URLForApplicationToOpenURL:")
101+
, sel_URLForApplicationToOpenContentType_("URLForApplicationToOpenContentType:")
100102
, sel_URLWithString_("URLWithString:")
101103
, sel_UTF8String("UTF8String")
102104
, sel_abortEditing("abortEditing")
@@ -1304,6 +1306,7 @@ public enum Selector {
13041306
, sel_type("type")
13051307
, sel_type_conformsToType_("type:conformsToType:")
13061308
, sel_typeOfFile_error_("typeOfFile:error:")
1309+
, sel_typeWithFilenameExtension_("typeWithFilenameExtension:")
13071310
, sel_types("types")
13081311
, sel_typesetter("typesetter")
13091312
, sel_unarchiveObjectWithData_("unarchiveObjectWithData:")

bundles/org.eclipse.swt/Eclipse SWT Program/cocoa/org/eclipse/swt/program/Program.java

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2017 IBM Corporation and others.
2+
* Copyright (c) 2000, 2026 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -73,15 +73,11 @@ public static Program findProgram (String extension) {
7373
}
7474
NSString ext = NSString.stringWithCharacters(chars, chars.length);
7575
if (ext != null) {
76-
byte[] fsRef = new byte[80];
77-
if (OS.LSGetApplicationForInfo(OS.kLSUnknownType, OS.kLSUnknownCreator, ext.id, OS.kLSRolesAll, fsRef, null) == OS.noErr) {
78-
long url = OS.CFURLCreateFromFSRef(OS.kCFAllocatorDefault(), fsRef);
79-
if (url != 0) {
80-
NSString bundlePath = new NSURL(url).path();
81-
NSBundle bundle = NSBundle.bundleWithPath(bundlePath);
82-
if (bundle != null) program = getProgram(bundle);
83-
OS.CFRelease(url);
84-
}
76+
NSWorkspace workspace = NSWorkspace.sharedWorkspace();
77+
NSURL appURL = findAppURLForExtension(workspace, ext);
78+
if (appURL != null) {
79+
NSBundle bundle = NSBundle.bundleWithPath(appURL.path());
80+
if (bundle != null) program = getProgram(bundle);
8581
}
8682
}
8783
return program;
@@ -153,6 +149,26 @@ public static Program findProgram (String extension) {
153149
}
154150
}
155151

152+
static NSURL findAppURLForExtension(NSWorkspace workspace, NSString ext) {
153+
// On macOS 12.0+, use the content type-based API which works reliably
154+
// for all file types including third-party ones.
155+
if (OS.VERSION >= OS.VERSION(12, 0, 0)) {
156+
long UTTypeClass = OS.objc_getClass("UTType");
157+
if (UTTypeClass != 0) {
158+
long utType = OS.objc_msgSend(UTTypeClass, OS.sel_typeWithFilenameExtension_, ext.id);
159+
if (utType != 0) {
160+
NSURL appURL = workspace.urlForApplicationToOpenContentType(utType);
161+
if (appURL != null) {
162+
return appURL;
163+
}
164+
}
165+
}
166+
}
167+
// Fallback: URL-based lookup (available since macOS 10.6, deprecated in macOS 12.0)
168+
NSURL fileURL = NSURL.fileURLWithPath(NSString.stringWith("/tmp/dummy." + ext.getString()));
169+
return workspace.urlForApplicationToOpenURL(fileURL);
170+
}
171+
156172
static Program getProgram(NSBundle bundle) {
157173
NSString CFBundleName = NSString.stringWith("CFBundleName");
158174
NSString CFBundleDisplayName = NSString.stringWith("CFBundleDisplayName");

tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_program_Program.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2025 IBM Corporation and others.
2+
* Copyright (c) 2000, 2026 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -18,6 +18,7 @@
1818
import static org.junit.jupiter.api.Assertions.assertNotNull;
1919
import static org.junit.jupiter.api.Assertions.assertThrows;
2020
import static org.junit.jupiter.api.Assertions.assertTrue;
21+
import static org.junit.jupiter.api.Assumptions.assumeFalse;
2122

2223
import java.util.HashSet;
2324
import java.util.Set;
@@ -90,6 +91,17 @@ public void test_findProgramLjava_lang_String() {
9091
assertSWTProblem("Failed to throw ERROR_NULL_ARGUMENT", SWT.ERROR_NULL_ARGUMENT, e);
9192
}
9293

94+
@Test
95+
public void test_findProgramForTxt() {
96+
// Applications for MIME types may not be registered in Linux test environments
97+
assumeFalse(SwtTestUtil.isGTK, "Applications for MIME types may not be registered in Linux test environments");
98+
// .txt files are always associated with a text editor on Windows and macOS
99+
Program program = Program.findProgram(".txt");
100+
assertNotNull(program, "Expected a program for .txt files");
101+
assertNotNull(program.getName(), "Expected program to have a non-null name");
102+
assertTrue(program.getName().length() > 0, "Expected program to have a non-empty name");
103+
}
104+
93105
@Test
94106
public void test_getExtensions() {
95107
String[] extensions = Program.getExtensions();

0 commit comments

Comments
 (0)