| // Copyright 2013 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "base/mac/launch_application.h" |
| |
| #include "base/command_line.h" |
| #include "base/functional/callback.h" |
| #include "base/logging.h" |
| #include "base/mac/foundation_util.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/types/expected.h" |
| |
| namespace base::mac { |
| |
| namespace { |
| |
| NSArray* CommandLineArgsToArgsArray(const CommandLineArgs& command_line_args) { |
| if (const CommandLine* command_line = |
| absl::get_if<CommandLine>(&command_line_args)) { |
| const auto& argv = command_line->argv(); |
| size_t argc = argv.size(); |
| DCHECK_GT(argc, 0lu); |
| |
| NSMutableArray* args_array = [NSMutableArray arrayWithCapacity:argc - 1]; |
| // NSWorkspace automatically adds the binary path as the first argument and |
| // thus it should not be included in the list. |
| for (size_t i = 1; i < argc; ++i) { |
| [args_array addObject:base::SysUTF8ToNSString(argv[i])]; |
| } |
| |
| return args_array; |
| } |
| |
| if (const std::vector<std::string>* string_vector = |
| absl::get_if<std::vector<std::string>>(&command_line_args)) { |
| NSMutableArray* args_array = |
| [NSMutableArray arrayWithCapacity:string_vector->size()]; |
| for (const auto& arg : *string_vector) { |
| [args_array addObject:base::SysUTF8ToNSString(arg)]; |
| } |
| } |
| |
| return @[]; |
| } |
| |
| NSWorkspaceOpenConfiguration* GetOpenConfiguration( |
| LaunchApplicationOptions options, |
| const CommandLineArgs& command_line_args) API_AVAILABLE(macos(10.15)) { |
| NSWorkspaceOpenConfiguration* config = |
| [NSWorkspaceOpenConfiguration configuration]; |
| |
| config.activates = options.activate; |
| config.createsNewApplicationInstance = options.create_new_instance; |
| config.promptsUserIfNeeded = options.prompt_user_if_needed; |
| config.arguments = CommandLineArgsToArgsArray(command_line_args); |
| |
| return config; |
| } |
| |
| NSWorkspaceLaunchOptions GetLaunchOptions(LaunchApplicationOptions options) { |
| NSWorkspaceLaunchOptions launch_options = NSWorkspaceLaunchDefault; |
| |
| if (!options.activate) { |
| launch_options |= NSWorkspaceLaunchWithoutActivation; |
| } |
| if (options.create_new_instance) { |
| launch_options |= NSWorkspaceLaunchNewInstance; |
| } |
| if (options.prompt_user_if_needed) { |
| launch_options |= NSWorkspaceLaunchWithErrorPresentation; |
| } |
| |
| return launch_options; |
| } |
| |
| } // namespace |
| |
| void LaunchApplication(const base::FilePath& app_bundle_path, |
| const CommandLineArgs& command_line_args, |
| const std::vector<std::string>& url_specs, |
| LaunchApplicationOptions options, |
| LaunchApplicationCallback callback) { |
| __block LaunchApplicationCallback callback_block_access = std::move(callback); |
| |
| NSURL* bundle_url = FilePathToNSURL(app_bundle_path); |
| if (!bundle_url) { |
| dispatch_async(dispatch_get_main_queue(), ^{ |
| std::move(callback_block_access) |
| .Run(base::unexpected([NSError errorWithDomain:NSCocoaErrorDomain |
| code:NSFileNoSuchFileError |
| userInfo:nil])); |
| }); |
| return; |
| } |
| |
| NSMutableArray* ns_urls = nil; |
| if (!url_specs.empty()) { |
| ns_urls = [NSMutableArray arrayWithCapacity:url_specs.size()]; |
| for (const auto& url_spec : url_specs) { |
| [ns_urls |
| addObject:[NSURL URLWithString:base::SysUTF8ToNSString(url_spec)]]; |
| } |
| } |
| |
| if (@available(macOS 10.15, *)) { |
| void (^action_block)(NSRunningApplication*, NSError*) = |
| ^void(NSRunningApplication* app, NSError* error) { |
| dispatch_async(dispatch_get_main_queue(), ^{ |
| if (error) { |
| LOG(ERROR) << base::SysNSStringToUTF8(error.localizedDescription); |
| std::move(callback_block_access).Run(base::unexpected(error)); |
| } else { |
| std::move(callback_block_access).Run(app); |
| } |
| }); |
| }; |
| |
| NSWorkspaceOpenConfiguration* configuration = |
| GetOpenConfiguration(options, command_line_args); |
| |
| if (ns_urls) { |
| [NSWorkspace.sharedWorkspace openURLs:ns_urls |
| withApplicationAtURL:bundle_url |
| configuration:configuration |
| completionHandler:action_block]; |
| } else { |
| [NSWorkspace.sharedWorkspace openApplicationAtURL:bundle_url |
| configuration:configuration |
| completionHandler:action_block]; |
| } |
| } else { |
| NSDictionary* configuration = @{ |
| NSWorkspaceLaunchConfigurationArguments : |
| CommandLineArgsToArgsArray(command_line_args), |
| }; |
| |
| NSWorkspaceLaunchOptions launch_options = GetLaunchOptions(options); |
| |
| NSError* error = nil; |
| NSRunningApplication* app; |
| if (ns_urls) { |
| app = [NSWorkspace.sharedWorkspace openURLs:ns_urls |
| withApplicationAtURL:bundle_url |
| options:launch_options |
| configuration:configuration |
| error:&error]; |
| } else { |
| app = [NSWorkspace.sharedWorkspace launchApplicationAtURL:bundle_url |
| options:launch_options |
| configuration:configuration |
| error:&error]; |
| } |
| |
| dispatch_async(dispatch_get_main_queue(), ^{ |
| if (error) { |
| LOG(ERROR) << base::SysNSStringToUTF8(error.localizedDescription); |
| std::move(callback_block_access).Run(base::unexpected(error)); |
| } else { |
| std::move(callback_block_access).Run(app); |
| } |
| }); |
| } |
| } |
| |
| } // namespace base::mac |