// 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(&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* string_vector = absl::get_if>(&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& 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