|
1 | 1 | /* |
2 | | - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | 4 | * |
5 | 5 | * This code is free software; you can redistribute it and/or modify it |
|
27 | 27 | #import <CoreFoundation/CoreFoundation.h> |
28 | 28 | #import <ApplicationServices/ApplicationServices.h> |
29 | 29 | #import <JavaNativeFoundation/JavaNativeFoundation.h> |
| 30 | +#import <AppKit/AppKit.h> |
| 31 | +#import "sun_lwawt_macosx_CDesktopPeer.h" |
30 | 32 |
|
31 | 33 | /* |
32 | 34 | * Class: sun_lwawt_macosx_CDesktopPeer |
33 | 35 | * Method: _lsOpenURI |
34 | | - * Signature: (Ljava/lang/String;)I; |
| 36 | + * Signature: (Ljava/lang/String;I)I |
35 | 37 | */ |
36 | 38 | JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_CDesktopPeer__1lsOpenURI |
37 | | -(JNIEnv *env, jclass clz, jstring uri) |
| 39 | +(JNIEnv *env, jclass clz, jstring uri, jint action) |
38 | 40 | { |
39 | | - OSStatus status = noErr; |
| 41 | + __block OSStatus status = noErr; |
40 | 42 | JNF_COCOA_ENTER(env); |
41 | 43 |
|
42 | | - // I would love to use NSWorkspace here, but it's not thread safe. Why? I don't know. |
43 | | - // So we use LaunchServices directly. |
44 | | - |
45 | | - NSURL *url = [NSURL URLWithString:JNFJavaToNSString(env, uri)]; |
46 | | - |
47 | | - LSLaunchFlags flags = kLSLaunchDefaults; |
48 | | - |
49 | | - LSApplicationParameters params = {0, flags, NULL, NULL, NULL, NULL, NULL}; |
50 | | - status = LSOpenURLsWithRole((CFArrayRef)[NSArray arrayWithObject:url], kLSRolesAll, NULL, ¶ms, NULL, 0); |
| 44 | + NSURL *urlToOpen = [NSURL URLWithString:JNFJavaToNSString(env, uri)]; |
| 45 | + NSURL *appURI = nil; |
| 46 | + |
| 47 | + if (action == sun_lwawt_macosx_CDesktopPeer_BROWSE) { |
| 48 | + // To get the defaultBrowser |
| 49 | + NSURL *httpsURL = [NSURL URLWithString:@"https://"]; |
| 50 | + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; |
| 51 | + appURI = [workspace URLForApplicationToOpenURL:httpsURL]; |
| 52 | + } else if (action == sun_lwawt_macosx_CDesktopPeer_MAIL) { |
| 53 | + // To get the default mailer |
| 54 | + NSURL *mailtoURL = [NSURL URLWithString:@"mailto://"]; |
| 55 | + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; |
| 56 | + appURI = [workspace URLForApplicationToOpenURL:mailtoURL]; |
| 57 | + } |
| 58 | + |
| 59 | + if (appURI == nil) { |
| 60 | + return -1; |
| 61 | + } |
| 62 | + |
| 63 | + // Prepare NSOpenConfig object |
| 64 | + NSArray<NSURL *> *urls = @[urlToOpen]; |
| 65 | + NSWorkspaceOpenConfiguration *configuration = [NSWorkspaceOpenConfiguration configuration]; |
| 66 | + configuration.activates = YES; // To bring app to foreground |
| 67 | + configuration.promptsUserIfNeeded = YES; // To allow macOS desktop prompts |
| 68 | + |
| 69 | + // dispatch semaphores used to wait for the completion handler to update and return status |
| 70 | + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); |
| 71 | + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)); // 1 second timeout |
| 72 | + |
| 73 | + // Asynchronous call to openURL |
| 74 | + [[NSWorkspace sharedWorkspace] openURLs:urls |
| 75 | + withApplicationAtURL:appURI |
| 76 | + configuration:configuration |
| 77 | + completionHandler:^(NSRunningApplication *app, NSError *error) { |
| 78 | + if (error) { |
| 79 | + status = (OSStatus) error.code; |
| 80 | + } |
| 81 | + dispatch_semaphore_signal(semaphore); |
| 82 | + }]; |
| 83 | + |
| 84 | + dispatch_semaphore_wait(semaphore, timeout); |
51 | 85 |
|
52 | 86 | JNF_COCOA_EXIT(env); |
53 | 87 | return status; |
|
56 | 90 | /* |
57 | 91 | * Class: sun_lwawt_macosx_CDesktopPeer |
58 | 92 | * Method: _lsOpenFile |
59 | | - * Signature: (Ljava/lang/String;Z)I; |
| 93 | + * Signature: (Ljava/lang/String;I;Ljava/lang/String;)I; |
60 | 94 | */ |
61 | 95 | JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_CDesktopPeer__1lsOpenFile |
62 | | -(JNIEnv *env, jclass clz, jstring jpath, jboolean print) |
| 96 | +(JNIEnv *env, jclass clz, jstring jpath, jint action, jstring jtmpTxtPath) |
63 | 97 | { |
64 | | - OSStatus status = noErr; |
| 98 | + __block OSStatus status = noErr; |
65 | 99 | JNF_COCOA_ENTER(env); |
66 | 100 |
|
67 | | - // I would love to use NSWorkspace here, but it's not thread safe. Why? I don't know. |
68 | | - // So we use LaunchServices directly. |
69 | | - |
70 | 101 | NSString *path = JNFNormalizedNSStringForPath(env, jpath); |
71 | | - |
72 | | - NSURL *url = [NSURL fileURLWithPath:(NSString *)path]; |
| 102 | + NSURL *urlToOpen = [NSURL fileURLWithPath:(NSString *)path]; |
73 | 103 |
|
74 | 104 | // This byzantine workaround is necesary, or else directories won't open in Finder |
75 | | - url = (NSURL *)CFURLCreateWithFileSystemPath(NULL, (CFStringRef)[url path], kCFURLPOSIXPathStyle, false); |
76 | | - |
77 | | - LSLaunchFlags flags = kLSLaunchDefaults; |
78 | | - if (print) flags |= kLSLaunchAndPrint; |
79 | | - |
80 | | - LSApplicationParameters params = {0, flags, NULL, NULL, NULL, NULL, NULL}; |
81 | | - status = LSOpenURLsWithRole((CFArrayRef)[NSArray arrayWithObject:url], kLSRolesAll, NULL, ¶ms, NULL, 0); |
82 | | - [url release]; |
| 105 | + urlToOpen = (NSURL *)CFURLCreateWithFileSystemPath(NULL, (CFStringRef)[urlToOpen path], |
| 106 | + kCFURLPOSIXPathStyle, false); |
| 107 | + |
| 108 | + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; |
| 109 | + NSURL *appURI = [workspace URLForApplicationToOpenURL:urlToOpen]; |
| 110 | + NSURL *defaultTerminalApp = [workspace URLForApplicationToOpenURL:[NSURL URLWithString:@"file:///bin/sh"]]; |
| 111 | + |
| 112 | + // Prepare NSOpenConfig object |
| 113 | + NSArray<NSURL *> *urls = @[urlToOpen]; |
| 114 | + NSWorkspaceOpenConfiguration *configuration = [NSWorkspaceOpenConfiguration configuration]; |
| 115 | + configuration.activates = YES; // To bring app to foreground |
| 116 | + configuration.promptsUserIfNeeded = YES; // To allow macOS desktop prompts |
| 117 | + |
| 118 | + // pre-checks for open/print/edit before calling openURLs API |
| 119 | + if (action == sun_lwawt_macosx_CDesktopPeer_OPEN |
| 120 | + || action == sun_lwawt_macosx_CDesktopPeer_PRINT) { |
| 121 | + if (appURI == nil |
| 122 | + || [[urlToOpen absoluteString] containsString:[appURI absoluteString]] |
| 123 | + || [[defaultTerminalApp absoluteString] containsString:[appURI absoluteString]]) { |
| 124 | + return -1; |
| 125 | + } |
| 126 | + // Additionally set forPrinting=TRUE for print |
| 127 | + if (action == sun_lwawt_macosx_CDesktopPeer_PRINT) { |
| 128 | + configuration.forPrinting = YES; |
| 129 | + } |
| 130 | + } else if (action == sun_lwawt_macosx_CDesktopPeer_EDIT) { |
| 131 | + if (appURI == nil |
| 132 | + || [[urlToOpen absoluteString] containsString:[appURI absoluteString]]) { |
| 133 | + return -1; |
| 134 | + } |
| 135 | + // for EDIT: if (defaultApp = TerminalApp) then set appURI = DefaultTextEditor |
| 136 | + if ([[defaultTerminalApp absoluteString] containsString:[appURI absoluteString]]) { |
| 137 | + NSString *path = JNFNormalizedNSStringForPath(env, jtmpTxtPath); |
| 138 | + NSURL *tempFilePath = [NSURL fileURLWithPath:(NSString *)path]; |
| 139 | + appURI = [workspace URLForApplicationToOpenURL:tempFilePath]; |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + // dispatch semaphores used to wait for the completion handler to update and return status |
| 144 | + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); |
| 145 | + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)); // 1 second timeout |
| 146 | + |
| 147 | + // Asynchronous call - openURLs:withApplicationAtURL |
| 148 | + [[NSWorkspace sharedWorkspace] openURLs:urls |
| 149 | + withApplicationAtURL:appURI |
| 150 | + configuration:configuration |
| 151 | + completionHandler:^(NSRunningApplication *app, NSError *error) { |
| 152 | + if (error) { |
| 153 | + status = (OSStatus) error.code; |
| 154 | + } |
| 155 | + dispatch_semaphore_signal(semaphore); |
| 156 | + }]; |
| 157 | + |
| 158 | + dispatch_semaphore_wait(semaphore, timeout); |
83 | 159 |
|
84 | 160 | JNF_COCOA_EXIT(env); |
85 | 161 | return status; |
|
0 commit comments