• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2013 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#import "base/mac/launch_application.h"
6
7#include "base/command_line.h"
8#include "base/functional/callback.h"
9#include "base/logging.h"
10#include "base/mac/foundation_util.h"
11#include "base/strings/sys_string_conversions.h"
12#include "base/types/expected.h"
13
14namespace base::mac {
15
16namespace {
17
18NSArray* CommandLineArgsToArgsArray(const CommandLineArgs& command_line_args) {
19  if (const CommandLine* command_line =
20          absl::get_if<CommandLine>(&command_line_args)) {
21    const auto& argv = command_line->argv();
22    size_t argc = argv.size();
23    DCHECK_GT(argc, 0lu);
24
25    NSMutableArray* args_array = [NSMutableArray arrayWithCapacity:argc - 1];
26    // NSWorkspace automatically adds the binary path as the first argument and
27    // thus it should not be included in the list.
28    for (size_t i = 1; i < argc; ++i) {
29      [args_array addObject:base::SysUTF8ToNSString(argv[i])];
30    }
31
32    return args_array;
33  }
34
35  if (const std::vector<std::string>* string_vector =
36          absl::get_if<std::vector<std::string>>(&command_line_args)) {
37    NSMutableArray* args_array =
38        [NSMutableArray arrayWithCapacity:string_vector->size()];
39    for (const auto& arg : *string_vector) {
40      [args_array addObject:base::SysUTF8ToNSString(arg)];
41    }
42  }
43
44  return @[];
45}
46
47NSWorkspaceOpenConfiguration* GetOpenConfiguration(
48    LaunchApplicationOptions options,
49    const CommandLineArgs& command_line_args) API_AVAILABLE(macos(10.15)) {
50  NSWorkspaceOpenConfiguration* config =
51      [NSWorkspaceOpenConfiguration configuration];
52
53  config.activates = options.activate;
54  config.createsNewApplicationInstance = options.create_new_instance;
55  config.promptsUserIfNeeded = options.prompt_user_if_needed;
56  config.arguments = CommandLineArgsToArgsArray(command_line_args);
57
58  return config;
59}
60
61NSWorkspaceLaunchOptions GetLaunchOptions(LaunchApplicationOptions options) {
62  NSWorkspaceLaunchOptions launch_options = NSWorkspaceLaunchDefault;
63
64  if (!options.activate) {
65    launch_options |= NSWorkspaceLaunchWithoutActivation;
66  }
67  if (options.create_new_instance) {
68    launch_options |= NSWorkspaceLaunchNewInstance;
69  }
70  if (options.prompt_user_if_needed) {
71    launch_options |= NSWorkspaceLaunchWithErrorPresentation;
72  }
73
74  return launch_options;
75}
76
77}  // namespace
78
79void LaunchApplication(const base::FilePath& app_bundle_path,
80                       const CommandLineArgs& command_line_args,
81                       const std::vector<std::string>& url_specs,
82                       LaunchApplicationOptions options,
83                       LaunchApplicationCallback callback) {
84  __block LaunchApplicationCallback callback_block_access = std::move(callback);
85
86  NSURL* bundle_url = FilePathToNSURL(app_bundle_path);
87  if (!bundle_url) {
88    dispatch_async(dispatch_get_main_queue(), ^{
89      std::move(callback_block_access)
90          .Run(base::unexpected([NSError errorWithDomain:NSCocoaErrorDomain
91                                                    code:NSFileNoSuchFileError
92                                                userInfo:nil]));
93    });
94    return;
95  }
96
97  NSMutableArray* ns_urls = nil;
98  if (!url_specs.empty()) {
99    ns_urls = [NSMutableArray arrayWithCapacity:url_specs.size()];
100    for (const auto& url_spec : url_specs) {
101      [ns_urls
102          addObject:[NSURL URLWithString:base::SysUTF8ToNSString(url_spec)]];
103    }
104  }
105
106  if (@available(macOS 10.15, *)) {
107    void (^action_block)(NSRunningApplication*, NSError*) =
108        ^void(NSRunningApplication* app, NSError* error) {
109          dispatch_async(dispatch_get_main_queue(), ^{
110            if (error) {
111              LOG(ERROR) << base::SysNSStringToUTF8(error.localizedDescription);
112              std::move(callback_block_access).Run(base::unexpected(error));
113            } else {
114              std::move(callback_block_access).Run(app);
115            }
116          });
117        };
118
119    NSWorkspaceOpenConfiguration* configuration =
120        GetOpenConfiguration(options, command_line_args);
121
122    if (ns_urls) {
123      [NSWorkspace.sharedWorkspace openURLs:ns_urls
124                       withApplicationAtURL:bundle_url
125                              configuration:configuration
126                          completionHandler:action_block];
127    } else {
128      [NSWorkspace.sharedWorkspace openApplicationAtURL:bundle_url
129                                          configuration:configuration
130                                      completionHandler:action_block];
131    }
132  } else {
133    NSDictionary* configuration = @{
134      NSWorkspaceLaunchConfigurationArguments :
135          CommandLineArgsToArgsArray(command_line_args),
136    };
137
138    NSWorkspaceLaunchOptions launch_options = GetLaunchOptions(options);
139
140    NSError* error = nil;
141    NSRunningApplication* app;
142    if (ns_urls) {
143      app = [NSWorkspace.sharedWorkspace openURLs:ns_urls
144                             withApplicationAtURL:bundle_url
145                                          options:launch_options
146                                    configuration:configuration
147                                            error:&error];
148    } else {
149      app = [NSWorkspace.sharedWorkspace launchApplicationAtURL:bundle_url
150                                                        options:launch_options
151                                                  configuration:configuration
152                                                          error:&error];
153    }
154
155    dispatch_async(dispatch_get_main_queue(), ^{
156      if (error) {
157        LOG(ERROR) << base::SysNSStringToUTF8(error.localizedDescription);
158        std::move(callback_block_access).Run(base::unexpected(error));
159      } else {
160        std::move(callback_block_access).Run(app);
161      }
162    });
163  }
164}
165
166}  // namespace base::mac
167