blob: beefe924eafbef8437d406a2347fa7624ce22248 [file] [log] [blame]
// 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