1*6777b538SAndroid Build Coastguard Worker// Copyright 2023 The Chromium Authors 2*6777b538SAndroid Build Coastguard Worker// Use of this source code is governed by a BSD-style license that can be 3*6777b538SAndroid Build Coastguard Worker// found in the LICENSE file. 4*6777b538SAndroid Build Coastguard Worker 5*6777b538SAndroid Build Coastguard Worker#include "base/mac/launch_application.h" 6*6777b538SAndroid Build Coastguard Worker 7*6777b538SAndroid Build Coastguard Worker#include <sys/select.h> 8*6777b538SAndroid Build Coastguard Worker 9*6777b538SAndroid Build Coastguard Worker#include "base/apple/bridging.h" 10*6777b538SAndroid Build Coastguard Worker#include "base/apple/foundation_util.h" 11*6777b538SAndroid Build Coastguard Worker#include "base/base_paths.h" 12*6777b538SAndroid Build Coastguard Worker#include "base/files/file_path.h" 13*6777b538SAndroid Build Coastguard Worker#include "base/files/file_util.h" 14*6777b538SAndroid Build Coastguard Worker#include "base/files/scoped_temp_dir.h" 15*6777b538SAndroid Build Coastguard Worker#include "base/functional/callback_helpers.h" 16*6777b538SAndroid Build Coastguard Worker#include "base/logging.h" 17*6777b538SAndroid Build Coastguard Worker#include "base/mac/mac_util.h" 18*6777b538SAndroid Build Coastguard Worker#include "base/path_service.h" 19*6777b538SAndroid Build Coastguard Worker#include "base/process/launch.h" 20*6777b538SAndroid Build Coastguard Worker#include "base/strings/string_util.h" 21*6777b538SAndroid Build Coastguard Worker#include "base/strings/sys_string_conversions.h" 22*6777b538SAndroid Build Coastguard Worker#include "base/task/bind_post_task.h" 23*6777b538SAndroid Build Coastguard Worker#include "base/task/thread_pool.h" 24*6777b538SAndroid Build Coastguard Worker#include "base/test/bind.h" 25*6777b538SAndroid Build Coastguard Worker#include "base/test/task_environment.h" 26*6777b538SAndroid Build Coastguard Worker#include "base/test/test_future.h" 27*6777b538SAndroid Build Coastguard Worker#include "base/threading/platform_thread.h" 28*6777b538SAndroid Build Coastguard Worker#include "base/types/expected.h" 29*6777b538SAndroid Build Coastguard Worker#include "base/uuid.h" 30*6777b538SAndroid Build Coastguard Worker#include "testing/gmock/include/gmock/gmock.h" 31*6777b538SAndroid Build Coastguard Worker#include "testing/gtest/include/gtest/gtest.h" 32*6777b538SAndroid Build Coastguard Worker#import "testing/gtest_mac.h" 33*6777b538SAndroid Build Coastguard Worker 34*6777b538SAndroid Build Coastguard Workernamespace base::mac { 35*6777b538SAndroid Build Coastguard Workernamespace { 36*6777b538SAndroid Build Coastguard Worker 37*6777b538SAndroid Build Coastguard Worker// Reads XML encoded property lists from `fifo_path`, calling `callback` for 38*6777b538SAndroid Build Coastguard Worker// each succesfully parsed dictionary. Loops indefinitely until the string 39*6777b538SAndroid Build Coastguard Worker// "<!FINISHED>" is read from `fifo_path`. 40*6777b538SAndroid Build Coastguard Workervoid ReadLaunchEventsFromFifo( 41*6777b538SAndroid Build Coastguard Worker const FilePath& fifo_path, 42*6777b538SAndroid Build Coastguard Worker RepeatingCallback<void(NSDictionary* event)> callback) { 43*6777b538SAndroid Build Coastguard Worker File f(fifo_path, File::FLAG_OPEN | File::FLAG_READ); 44*6777b538SAndroid Build Coastguard Worker std::string data; 45*6777b538SAndroid Build Coastguard Worker while (true) { 46*6777b538SAndroid Build Coastguard Worker char buf[4096]; 47*6777b538SAndroid Build Coastguard Worker int read_count = f.ReadAtCurrentPosNoBestEffort(buf, sizeof buf); 48*6777b538SAndroid Build Coastguard Worker if (read_count) { 49*6777b538SAndroid Build Coastguard Worker data += std::string(buf, read_count); 50*6777b538SAndroid Build Coastguard Worker // Assume that at any point the beginning of the data buffer is the start 51*6777b538SAndroid Build Coastguard Worker // of a plist. Search for the first end, and parse that substring. 52*6777b538SAndroid Build Coastguard Worker size_t end_of_plist; 53*6777b538SAndroid Build Coastguard Worker while ((end_of_plist = data.find("</plist>")) != std::string::npos) { 54*6777b538SAndroid Build Coastguard Worker std::string plist = data.substr(0, end_of_plist + 8); 55*6777b538SAndroid Build Coastguard Worker data = data.substr(plist.length()); 56*6777b538SAndroid Build Coastguard Worker NSDictionary* event = apple::ObjCCastStrict<NSDictionary>( 57*6777b538SAndroid Build Coastguard Worker SysUTF8ToNSString(TrimWhitespaceASCII(plist, TRIM_ALL)) 58*6777b538SAndroid Build Coastguard Worker .propertyList); 59*6777b538SAndroid Build Coastguard Worker callback.Run(event); 60*6777b538SAndroid Build Coastguard Worker } 61*6777b538SAndroid Build Coastguard Worker // No more plists found, check if the termination marker was send. 62*6777b538SAndroid Build Coastguard Worker if (data.find("<!FINISHED>") != std::string::npos) { 63*6777b538SAndroid Build Coastguard Worker break; 64*6777b538SAndroid Build Coastguard Worker } 65*6777b538SAndroid Build Coastguard Worker } else { 66*6777b538SAndroid Build Coastguard Worker // No data was read, wait for the file descriptor to become readable 67*6777b538SAndroid Build Coastguard Worker // again. 68*6777b538SAndroid Build Coastguard Worker fd_set fds; 69*6777b538SAndroid Build Coastguard Worker FD_ZERO(&fds); 70*6777b538SAndroid Build Coastguard Worker FD_SET(f.GetPlatformFile(), &fds); 71*6777b538SAndroid Build Coastguard Worker select(FD_SETSIZE, &fds, nullptr, nullptr, nullptr); 72*6777b538SAndroid Build Coastguard Worker } 73*6777b538SAndroid Build Coastguard Worker } 74*6777b538SAndroid Build Coastguard Worker} 75*6777b538SAndroid Build Coastguard Worker 76*6777b538SAndroid Build Coastguard Worker// This test harness creates an app bundle with a random bundle identifier to 77*6777b538SAndroid Build Coastguard Worker// avoid conflicts with concurrently running other tests. The binary in this app 78*6777b538SAndroid Build Coastguard Worker// bundle writes various events to a named pipe, allowing tests here to verify 79*6777b538SAndroid Build Coastguard Worker// that correct events were received by the app. 80*6777b538SAndroid Build Coastguard Workerclass LaunchApplicationTest : public testing::Test { 81*6777b538SAndroid Build Coastguard Worker public: 82*6777b538SAndroid Build Coastguard Worker void SetUp() override { 83*6777b538SAndroid Build Coastguard Worker helper_bundle_id_ = 84*6777b538SAndroid Build Coastguard Worker SysUTF8ToNSString("org.chromium.LaunchApplicationTestHelper." + 85*6777b538SAndroid Build Coastguard Worker Uuid::GenerateRandomV4().AsLowercaseString()); 86*6777b538SAndroid Build Coastguard Worker 87*6777b538SAndroid Build Coastguard Worker FilePath data_root; 88*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(PathService::Get(DIR_OUT_TEST_DATA_ROOT, &data_root)); 89*6777b538SAndroid Build Coastguard Worker const FilePath helper_app_executable = 90*6777b538SAndroid Build Coastguard Worker data_root.AppendASCII("launch_application_test_helper"); 91*6777b538SAndroid Build Coastguard Worker 92*6777b538SAndroid Build Coastguard Worker // Put helper app inside home dir, as the default temp location gets special 93*6777b538SAndroid Build Coastguard Worker // treatment by launch services, effecting the behavior of some of these 94*6777b538SAndroid Build Coastguard Worker // tests. 95*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(temp_dir_.CreateUniqueTempDirUnderPath(base::GetHomeDir())); 96*6777b538SAndroid Build Coastguard Worker 97*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_ = 98*6777b538SAndroid Build Coastguard Worker temp_dir_.GetPath().AppendASCII("launch_application_test_helper.app"); 99*6777b538SAndroid Build Coastguard Worker 100*6777b538SAndroid Build Coastguard Worker const base::FilePath destination_contents_path = 101*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_.AppendASCII("Contents"); 102*6777b538SAndroid Build Coastguard Worker const base::FilePath destination_executable_path = 103*6777b538SAndroid Build Coastguard Worker destination_contents_path.AppendASCII("MacOS"); 104*6777b538SAndroid Build Coastguard Worker 105*6777b538SAndroid Build Coastguard Worker // First create the .app bundle directory structure. 106*6777b538SAndroid Build Coastguard Worker // Use NSFileManager so that the permissions can be set appropriately. The 107*6777b538SAndroid Build Coastguard Worker // base::CreateDirectory() routine forces mode 0700. 108*6777b538SAndroid Build Coastguard Worker NSError* error = nil; 109*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE([NSFileManager.defaultManager 110*6777b538SAndroid Build Coastguard Worker createDirectoryAtURL:base::apple::FilePathToNSURL( 111*6777b538SAndroid Build Coastguard Worker destination_executable_path) 112*6777b538SAndroid Build Coastguard Worker withIntermediateDirectories:YES 113*6777b538SAndroid Build Coastguard Worker attributes:@{ 114*6777b538SAndroid Build Coastguard Worker NSFilePosixPermissions : @(0755) 115*6777b538SAndroid Build Coastguard Worker } 116*6777b538SAndroid Build Coastguard Worker error:&error]) 117*6777b538SAndroid Build Coastguard Worker << SysNSStringToUTF8(error.description); 118*6777b538SAndroid Build Coastguard Worker 119*6777b538SAndroid Build Coastguard Worker // Copy the executable file. 120*6777b538SAndroid Build Coastguard Worker helper_app_executable_path_ = 121*6777b538SAndroid Build Coastguard Worker destination_executable_path.Append(helper_app_executable.BaseName()); 122*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE( 123*6777b538SAndroid Build Coastguard Worker base::CopyFile(helper_app_executable, helper_app_executable_path_)); 124*6777b538SAndroid Build Coastguard Worker 125*6777b538SAndroid Build Coastguard Worker // Write the PkgInfo file. 126*6777b538SAndroid Build Coastguard Worker constexpr char kPkgInfoData[] = "APPL????"; 127*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(base::WriteFile( 128*6777b538SAndroid Build Coastguard Worker destination_contents_path.AppendASCII("PkgInfo"), kPkgInfoData)); 129*6777b538SAndroid Build Coastguard Worker 130*6777b538SAndroid Build Coastguard Worker#if defined(ADDRESS_SANITIZER) 131*6777b538SAndroid Build Coastguard Worker const base::FilePath asan_library_path = 132*6777b538SAndroid Build Coastguard Worker data_root.AppendASCII("libclang_rt.asan_osx_dynamic.dylib"); 133*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(base::CopyFile( 134*6777b538SAndroid Build Coastguard Worker asan_library_path, 135*6777b538SAndroid Build Coastguard Worker destination_executable_path.Append(asan_library_path.BaseName()))); 136*6777b538SAndroid Build Coastguard Worker#endif 137*6777b538SAndroid Build Coastguard Worker 138*6777b538SAndroid Build Coastguard Worker // Generate the Plist file 139*6777b538SAndroid Build Coastguard Worker NSDictionary* plist = @{ 140*6777b538SAndroid Build Coastguard Worker @"CFBundleExecutable" : 141*6777b538SAndroid Build Coastguard Worker apple::FilePathToNSString(helper_app_executable.BaseName()), 142*6777b538SAndroid Build Coastguard Worker @"CFBundleIdentifier" : helper_bundle_id_, 143*6777b538SAndroid Build Coastguard Worker }; 144*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE([plist 145*6777b538SAndroid Build Coastguard Worker writeToURL:apple::FilePathToNSURL( 146*6777b538SAndroid Build Coastguard Worker destination_contents_path.AppendASCII("Info.plist")) 147*6777b538SAndroid Build Coastguard Worker error:nil]); 148*6777b538SAndroid Build Coastguard Worker 149*6777b538SAndroid Build Coastguard Worker // Register the app with LaunchServices. 150*6777b538SAndroid Build Coastguard Worker LSRegisterURL(base::apple::FilePathToCFURL(helper_app_bundle_path_).get(), 151*6777b538SAndroid Build Coastguard Worker true); 152*6777b538SAndroid Build Coastguard Worker 153*6777b538SAndroid Build Coastguard Worker // Ensure app was registered with LaunchServices. Sometimes it takes a 154*6777b538SAndroid Build Coastguard Worker // little bit of time for this to happen, and some tests might fail if the 155*6777b538SAndroid Build Coastguard Worker // app wasn't registered yet. 156*6777b538SAndroid Build Coastguard Worker while (true) { 157*6777b538SAndroid Build Coastguard Worker NSArray<NSURL*>* apps = nil; 158*6777b538SAndroid Build Coastguard Worker if (@available(macOS 12.0, *)) { 159*6777b538SAndroid Build Coastguard Worker apps = [[NSWorkspace sharedWorkspace] 160*6777b538SAndroid Build Coastguard Worker URLsForApplicationsWithBundleIdentifier:helper_bundle_id_]; 161*6777b538SAndroid Build Coastguard Worker } else { 162*6777b538SAndroid Build Coastguard Worker apps = 163*6777b538SAndroid Build Coastguard Worker apple::CFToNSOwnershipCast(LSCopyApplicationURLsForBundleIdentifier( 164*6777b538SAndroid Build Coastguard Worker apple::NSToCFPtrCast(helper_bundle_id_), /*outError=*/nullptr)); 165*6777b538SAndroid Build Coastguard Worker } 166*6777b538SAndroid Build Coastguard Worker if (apps.count > 0) { 167*6777b538SAndroid Build Coastguard Worker break; 168*6777b538SAndroid Build Coastguard Worker } 169*6777b538SAndroid Build Coastguard Worker PlatformThread::Sleep(Milliseconds(50)); 170*6777b538SAndroid Build Coastguard Worker } 171*6777b538SAndroid Build Coastguard Worker 172*6777b538SAndroid Build Coastguard Worker // Setup fifo to receive logs from the helper app. 173*6777b538SAndroid Build Coastguard Worker helper_app_fifo_path_ = 174*6777b538SAndroid Build Coastguard Worker temp_dir_.GetPath().AppendASCII("launch_application_test_helper.fifo"); 175*6777b538SAndroid Build Coastguard Worker ASSERT_EQ(0, mkfifo(helper_app_fifo_path_.value().c_str(), 176*6777b538SAndroid Build Coastguard Worker S_IWUSR | S_IRUSR | S_IWGRP | S_IWGRP)); 177*6777b538SAndroid Build Coastguard Worker 178*6777b538SAndroid Build Coastguard Worker // Create array to store received events in, and start listening for events. 179*6777b538SAndroid Build Coastguard Worker launch_events_ = [[NSMutableArray alloc] init]; 180*6777b538SAndroid Build Coastguard Worker base::ThreadPool::PostTask( 181*6777b538SAndroid Build Coastguard Worker FROM_HERE, {MayBlock()}, 182*6777b538SAndroid Build Coastguard Worker base::BindOnce( 183*6777b538SAndroid Build Coastguard Worker &ReadLaunchEventsFromFifo, helper_app_fifo_path_, 184*6777b538SAndroid Build Coastguard Worker BindPostTaskToCurrentDefault(BindRepeating( 185*6777b538SAndroid Build Coastguard Worker &LaunchApplicationTest::OnLaunchEvent, Unretained(this))))); 186*6777b538SAndroid Build Coastguard Worker } 187*6777b538SAndroid Build Coastguard Worker 188*6777b538SAndroid Build Coastguard Worker void TearDown() override { 189*6777b538SAndroid Build Coastguard Worker if (temp_dir_.IsValid()) { 190*6777b538SAndroid Build Coastguard Worker // Make sure fifo reading task stops reading/waiting. 191*6777b538SAndroid Build Coastguard Worker WriteFile(helper_app_fifo_path_, "<!FINISHED>"); 192*6777b538SAndroid Build Coastguard Worker 193*6777b538SAndroid Build Coastguard Worker // Make sure all apps that were launched by this test are terminated. 194*6777b538SAndroid Build Coastguard Worker NSArray<NSRunningApplication*>* apps = 195*6777b538SAndroid Build Coastguard Worker NSWorkspace.sharedWorkspace.runningApplications; 196*6777b538SAndroid Build Coastguard Worker for (NSRunningApplication* app in apps) { 197*6777b538SAndroid Build Coastguard Worker if (temp_dir_.GetPath().IsParent( 198*6777b538SAndroid Build Coastguard Worker apple::NSURLToFilePath(app.bundleURL)) || 199*6777b538SAndroid Build Coastguard Worker [app.bundleIdentifier isEqualToString:helper_bundle_id_]) { 200*6777b538SAndroid Build Coastguard Worker [app forceTerminate]; 201*6777b538SAndroid Build Coastguard Worker } 202*6777b538SAndroid Build Coastguard Worker } 203*6777b538SAndroid Build Coastguard Worker 204*6777b538SAndroid Build Coastguard Worker // And make sure the temp dir was successfully deleted. 205*6777b538SAndroid Build Coastguard Worker EXPECT_TRUE(temp_dir_.Delete()); 206*6777b538SAndroid Build Coastguard Worker } 207*6777b538SAndroid Build Coastguard Worker } 208*6777b538SAndroid Build Coastguard Worker 209*6777b538SAndroid Build Coastguard Worker // Calls `LaunchApplication` with the given parameters, expecting the launch 210*6777b538SAndroid Build Coastguard Worker // to succeed. Returns the `NSRunningApplication*` the callback passed to 211*6777b538SAndroid Build Coastguard Worker // `LaunchApplication` was called with. 212*6777b538SAndroid Build Coastguard Worker NSRunningApplication* LaunchApplicationSyncExpectSuccess( 213*6777b538SAndroid Build Coastguard Worker const FilePath& app_bundle_path, 214*6777b538SAndroid Build Coastguard Worker const CommandLineArgs& command_line_args, 215*6777b538SAndroid Build Coastguard Worker const std::vector<std::string>& url_specs, 216*6777b538SAndroid Build Coastguard Worker LaunchApplicationOptions options) { 217*6777b538SAndroid Build Coastguard Worker test::TestFuture<NSRunningApplication*, NSError*> result; 218*6777b538SAndroid Build Coastguard Worker LaunchApplication(app_bundle_path, command_line_args, url_specs, options, 219*6777b538SAndroid Build Coastguard Worker result.GetCallback()); 220*6777b538SAndroid Build Coastguard Worker EXPECT_FALSE(result.Get<1>()); 221*6777b538SAndroid Build Coastguard Worker EXPECT_TRUE(result.Get<0>()); 222*6777b538SAndroid Build Coastguard Worker return result.Get<0>(); 223*6777b538SAndroid Build Coastguard Worker } 224*6777b538SAndroid Build Coastguard Worker 225*6777b538SAndroid Build Coastguard Worker // Similar to the above method, except that this version expects the launch to 226*6777b538SAndroid Build Coastguard Worker // fail, returning the error. 227*6777b538SAndroid Build Coastguard Worker NSError* LaunchApplicationSyncExpectError( 228*6777b538SAndroid Build Coastguard Worker const FilePath& app_bundle_path, 229*6777b538SAndroid Build Coastguard Worker const CommandLineArgs& command_line_args, 230*6777b538SAndroid Build Coastguard Worker const std::vector<std::string>& url_specs, 231*6777b538SAndroid Build Coastguard Worker LaunchApplicationOptions options) { 232*6777b538SAndroid Build Coastguard Worker test::TestFuture<NSRunningApplication*, NSError*> result; 233*6777b538SAndroid Build Coastguard Worker LaunchApplication(app_bundle_path, command_line_args, url_specs, options, 234*6777b538SAndroid Build Coastguard Worker result.GetCallback()); 235*6777b538SAndroid Build Coastguard Worker EXPECT_FALSE(result.Get<0>()); 236*6777b538SAndroid Build Coastguard Worker EXPECT_TRUE(result.Get<1>()); 237*6777b538SAndroid Build Coastguard Worker return result.Get<1>(); 238*6777b538SAndroid Build Coastguard Worker } 239*6777b538SAndroid Build Coastguard Worker 240*6777b538SAndroid Build Coastguard Worker // Waits for the total number of received launch events to reach at least 241*6777b538SAndroid Build Coastguard Worker // `expected_count`. 242*6777b538SAndroid Build Coastguard Worker void WaitForLaunchEvents(unsigned expected_count) { 243*6777b538SAndroid Build Coastguard Worker if (LaunchEventCount() >= expected_count) { 244*6777b538SAndroid Build Coastguard Worker return; 245*6777b538SAndroid Build Coastguard Worker } 246*6777b538SAndroid Build Coastguard Worker base::RunLoop loop; 247*6777b538SAndroid Build Coastguard Worker launch_event_callback_ = BindLambdaForTesting([&]() { 248*6777b538SAndroid Build Coastguard Worker if (LaunchEventCount() >= expected_count) { 249*6777b538SAndroid Build Coastguard Worker launch_event_callback_ = NullCallback(); 250*6777b538SAndroid Build Coastguard Worker loop.Quit(); 251*6777b538SAndroid Build Coastguard Worker } 252*6777b538SAndroid Build Coastguard Worker }); 253*6777b538SAndroid Build Coastguard Worker loop.Run(); 254*6777b538SAndroid Build Coastguard Worker } 255*6777b538SAndroid Build Coastguard Worker 256*6777b538SAndroid Build Coastguard Worker unsigned LaunchEventCount() { return launch_events_.count; } 257*6777b538SAndroid Build Coastguard Worker NSString* LaunchEventName(unsigned i) { 258*6777b538SAndroid Build Coastguard Worker if (i >= launch_events_.count) { 259*6777b538SAndroid Build Coastguard Worker return nil; 260*6777b538SAndroid Build Coastguard Worker } 261*6777b538SAndroid Build Coastguard Worker return apple::ObjCCastStrict<NSString>(launch_events_[i][@"name"]); 262*6777b538SAndroid Build Coastguard Worker } 263*6777b538SAndroid Build Coastguard Worker NSDictionary* LaunchEventData(unsigned i) { 264*6777b538SAndroid Build Coastguard Worker if (i >= launch_events_.count) { 265*6777b538SAndroid Build Coastguard Worker return nil; 266*6777b538SAndroid Build Coastguard Worker } 267*6777b538SAndroid Build Coastguard Worker return apple::ObjCCastStrict<NSDictionary>(launch_events_[i][@"data"]); 268*6777b538SAndroid Build Coastguard Worker } 269*6777b538SAndroid Build Coastguard Worker 270*6777b538SAndroid Build Coastguard Worker protected: 271*6777b538SAndroid Build Coastguard Worker ScopedTempDir temp_dir_; 272*6777b538SAndroid Build Coastguard Worker 273*6777b538SAndroid Build Coastguard Worker NSString* helper_bundle_id_; 274*6777b538SAndroid Build Coastguard Worker FilePath helper_app_bundle_path_; 275*6777b538SAndroid Build Coastguard Worker FilePath helper_app_executable_path_; 276*6777b538SAndroid Build Coastguard Worker FilePath helper_app_fifo_path_; 277*6777b538SAndroid Build Coastguard Worker 278*6777b538SAndroid Build Coastguard Worker NSMutableArray<NSDictionary*>* launch_events_; 279*6777b538SAndroid Build Coastguard Worker RepeatingClosure launch_event_callback_; 280*6777b538SAndroid Build Coastguard Worker 281*6777b538SAndroid Build Coastguard Worker test::TaskEnvironment task_environment_{ 282*6777b538SAndroid Build Coastguard Worker test::TaskEnvironment::MainThreadType::UI}; 283*6777b538SAndroid Build Coastguard Worker 284*6777b538SAndroid Build Coastguard Worker private: 285*6777b538SAndroid Build Coastguard Worker void OnLaunchEvent(NSDictionary* event) { 286*6777b538SAndroid Build Coastguard Worker NSLog(@"Event: %@", event); 287*6777b538SAndroid Build Coastguard Worker [launch_events_ addObject:event]; 288*6777b538SAndroid Build Coastguard Worker if (launch_event_callback_) { 289*6777b538SAndroid Build Coastguard Worker launch_event_callback_.Run(); 290*6777b538SAndroid Build Coastguard Worker } 291*6777b538SAndroid Build Coastguard Worker } 292*6777b538SAndroid Build Coastguard Worker}; 293*6777b538SAndroid Build Coastguard Worker 294*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, Basic) { 295*6777b538SAndroid Build Coastguard Worker std::vector<std::string> command_line_args; 296*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app = LaunchApplicationSyncExpectSuccess( 297*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {}, {}); 298*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(app); 299*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(app.bundleIdentifier, helper_bundle_id_); 300*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(apple::NSURLToFilePath(app.bundleURL), helper_app_bundle_path_); 301*6777b538SAndroid Build Coastguard Worker 302*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(1); 303*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(0), @"applicationDidFinishLaunching"); 304*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(0)[@"activationPolicy"], 305*6777b538SAndroid Build Coastguard Worker @(NSApplicationActivationPolicyRegular)); 306*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyRegular); 307*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(0)[@"commandLine"], 308*6777b538SAndroid Build Coastguard Worker (@[ apple::FilePathToNSString(helper_app_executable_path_) ])); 309*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(0)[@"processIdentifier"], 310*6777b538SAndroid Build Coastguard Worker @(app.processIdentifier)); 311*6777b538SAndroid Build Coastguard Worker} 312*6777b538SAndroid Build Coastguard Worker 313*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, BundleDoesntExist) { 314*6777b538SAndroid Build Coastguard Worker std::vector<std::string> command_line_args; 315*6777b538SAndroid Build Coastguard Worker NSError* err = LaunchApplicationSyncExpectError( 316*6777b538SAndroid Build Coastguard Worker temp_dir_.GetPath().AppendASCII("notexists.app"), command_line_args, {}, 317*6777b538SAndroid Build Coastguard Worker {}); 318*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(err); 319*6777b538SAndroid Build Coastguard Worker err = LaunchApplicationSyncExpectError( 320*6777b538SAndroid Build Coastguard Worker temp_dir_.GetPath().AppendASCII("notexists.app"), command_line_args, {}, 321*6777b538SAndroid Build Coastguard Worker {.hidden_in_background = true}); 322*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(err); 323*6777b538SAndroid Build Coastguard Worker} 324*6777b538SAndroid Build Coastguard Worker 325*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, BundleCorrupt) { 326*6777b538SAndroid Build Coastguard Worker base::DeleteFile(helper_app_executable_path_); 327*6777b538SAndroid Build Coastguard Worker std::vector<std::string> command_line_args; 328*6777b538SAndroid Build Coastguard Worker NSError* err = LaunchApplicationSyncExpectError(helper_app_bundle_path_, 329*6777b538SAndroid Build Coastguard Worker command_line_args, {}, {}); 330*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(err); 331*6777b538SAndroid Build Coastguard Worker err = LaunchApplicationSyncExpectError(helper_app_bundle_path_, 332*6777b538SAndroid Build Coastguard Worker command_line_args, {}, 333*6777b538SAndroid Build Coastguard Worker {.hidden_in_background = true}); 334*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(err); 335*6777b538SAndroid Build Coastguard Worker} 336*6777b538SAndroid Build Coastguard Worker 337*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, CommandLineArgs_StringVector) { 338*6777b538SAndroid Build Coastguard Worker std::vector<std::string> command_line_args = {"--foo", "bar", "-v"}; 339*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app = LaunchApplicationSyncExpectSuccess( 340*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {}, {}); 341*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(app); 342*6777b538SAndroid Build Coastguard Worker 343*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(1); 344*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(0), @"applicationDidFinishLaunching"); 345*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(0)[@"commandLine"], (@[ 346*6777b538SAndroid Build Coastguard Worker apple::FilePathToNSString(helper_app_executable_path_), 347*6777b538SAndroid Build Coastguard Worker @"--foo", @"bar", @"-v" 348*6777b538SAndroid Build Coastguard Worker ])); 349*6777b538SAndroid Build Coastguard Worker} 350*6777b538SAndroid Build Coastguard Worker 351*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, CommandLineArgs_BaseCommandLine) { 352*6777b538SAndroid Build Coastguard Worker CommandLine command_line(CommandLine::NO_PROGRAM); 353*6777b538SAndroid Build Coastguard Worker command_line.AppendSwitchASCII("foo", "bar"); 354*6777b538SAndroid Build Coastguard Worker command_line.AppendSwitch("v"); 355*6777b538SAndroid Build Coastguard Worker command_line.AppendSwitchPath("path", FilePath("/tmp")); 356*6777b538SAndroid Build Coastguard Worker 357*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app = LaunchApplicationSyncExpectSuccess( 358*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line, {}, {}); 359*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(app); 360*6777b538SAndroid Build Coastguard Worker 361*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(1); 362*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(0), @"applicationDidFinishLaunching"); 363*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(0)[@"commandLine"], (@[ 364*6777b538SAndroid Build Coastguard Worker apple::FilePathToNSString(helper_app_executable_path_), 365*6777b538SAndroid Build Coastguard Worker @"--foo=bar", @"--v", @"--path=/tmp" 366*6777b538SAndroid Build Coastguard Worker ])); 367*6777b538SAndroid Build Coastguard Worker} 368*6777b538SAndroid Build Coastguard Worker 369*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, UrlSpecs) { 370*6777b538SAndroid Build Coastguard Worker std::vector<std::string> command_line_args; 371*6777b538SAndroid Build Coastguard Worker std::vector<std::string> urls = {"https://example.com", 372*6777b538SAndroid Build Coastguard Worker "x-chrome-launch://1"}; 373*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app = LaunchApplicationSyncExpectSuccess( 374*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, urls, {}); 375*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(app); 376*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(3); 377*6777b538SAndroid Build Coastguard Worker 378*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(0), @"openURLs"); 379*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(1), @"applicationDidFinishLaunching"); 380*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(2), @"openURLs"); 381*6777b538SAndroid Build Coastguard Worker 382*6777b538SAndroid Build Coastguard Worker if (MacOSMajorVersion() == 11) { 383*6777b538SAndroid Build Coastguard Worker // macOS 11 (and only macOS 11) appears to sometimes trigger the openURLs 384*6777b538SAndroid Build Coastguard Worker // calls in reverse order. 385*6777b538SAndroid Build Coastguard Worker std::vector<std::string> received_urls; 386*6777b538SAndroid Build Coastguard Worker for (NSString* url in apple::ObjCCastStrict<NSArray>( 387*6777b538SAndroid Build Coastguard Worker LaunchEventData(0)[@"urls"])) { 388*6777b538SAndroid Build Coastguard Worker received_urls.push_back(SysNSStringToUTF8(url)); 389*6777b538SAndroid Build Coastguard Worker } 390*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(received_urls.size(), 1u); 391*6777b538SAndroid Build Coastguard Worker for (NSString* url in apple::ObjCCastStrict<NSArray>( 392*6777b538SAndroid Build Coastguard Worker LaunchEventData(2)[@"urls"])) { 393*6777b538SAndroid Build Coastguard Worker received_urls.push_back(SysNSStringToUTF8(url)); 394*6777b538SAndroid Build Coastguard Worker } 395*6777b538SAndroid Build Coastguard Worker EXPECT_THAT(received_urls, testing::UnorderedElementsAreArray(urls)); 396*6777b538SAndroid Build Coastguard Worker } else { 397*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(0)[@"urls"], @[ @"https://example.com" ]); 398*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(2)[@"urls"], @[ @"x-chrome-launch://1" ]); 399*6777b538SAndroid Build Coastguard Worker } 400*6777b538SAndroid Build Coastguard Worker} 401*6777b538SAndroid Build Coastguard Worker 402*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, CreateNewInstance) { 403*6777b538SAndroid Build Coastguard Worker std::vector<std::string> command_line_args; 404*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app1 = LaunchApplicationSyncExpectSuccess( 405*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {}, 406*6777b538SAndroid Build Coastguard Worker {.create_new_instance = false}); 407*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(1); 408*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(0), @"applicationDidFinishLaunching"); 409*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(0)[@"processIdentifier"], 410*6777b538SAndroid Build Coastguard Worker @(app1.processIdentifier)); 411*6777b538SAndroid Build Coastguard Worker 412*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app2 = LaunchApplicationSyncExpectSuccess( 413*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {"x-chrome-launch://0"}, 414*6777b538SAndroid Build Coastguard Worker {.create_new_instance = false}); 415*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(app1, app2); 416*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app1.processIdentifier, app2.processIdentifier); 417*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(2); 418*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(1), @"openURLs"); 419*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(1)[@"processIdentifier"], 420*6777b538SAndroid Build Coastguard Worker @(app2.processIdentifier)); 421*6777b538SAndroid Build Coastguard Worker 422*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app3 = LaunchApplicationSyncExpectSuccess( 423*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {"x-chrome-launch://1"}, 424*6777b538SAndroid Build Coastguard Worker {.create_new_instance = true}); 425*6777b538SAndroid Build Coastguard Worker EXPECT_NSNE(app1, app3); 426*6777b538SAndroid Build Coastguard Worker EXPECT_NE(app1.processIdentifier, app3.processIdentifier); 427*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(4); 428*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(2), @"openURLs"); 429*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(3), @"applicationDidFinishLaunching"); 430*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(3)[@"processIdentifier"], 431*6777b538SAndroid Build Coastguard Worker @(app3.processIdentifier)); 432*6777b538SAndroid Build Coastguard Worker} 433*6777b538SAndroid Build Coastguard Worker 434*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, HiddenInBackground) { 435*6777b538SAndroid Build Coastguard Worker std::vector<std::string> command_line_args = {"--test", "--foo"}; 436*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app = LaunchApplicationSyncExpectSuccess( 437*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {}, 438*6777b538SAndroid Build Coastguard Worker {.hidden_in_background = true}); 439*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(app); 440*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(app.bundleIdentifier, helper_bundle_id_); 441*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(helper_app_bundle_path_, apple::NSURLToFilePath(app.bundleURL)); 442*6777b538SAndroid Build Coastguard Worker 443*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(1); 444*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(0), @"applicationDidFinishLaunching"); 445*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(0)[@"activationPolicy"], 446*6777b538SAndroid Build Coastguard Worker @(NSApplicationActivationPolicyProhibited)); 447*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyProhibited); 448*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(0)[@"commandLine"], (@[ 449*6777b538SAndroid Build Coastguard Worker apple::FilePathToNSString(helper_app_executable_path_), 450*6777b538SAndroid Build Coastguard Worker @"--test", @"--foo" 451*6777b538SAndroid Build Coastguard Worker ])); 452*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(0)[@"processIdentifier"], 453*6777b538SAndroid Build Coastguard Worker @(app.processIdentifier)); 454*6777b538SAndroid Build Coastguard Worker 455*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app2 = LaunchApplicationSyncExpectSuccess( 456*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {}, 457*6777b538SAndroid Build Coastguard Worker {.create_new_instance = false, .hidden_in_background = true}); 458*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(app, app2); 459*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app.processIdentifier, app2.processIdentifier); 460*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyProhibited); 461*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app2.activationPolicy, NSApplicationActivationPolicyProhibited); 462*6777b538SAndroid Build Coastguard Worker // Launching without opening anything should not trigger any launch events. 463*6777b538SAndroid Build Coastguard Worker 464*6777b538SAndroid Build Coastguard Worker // Opening a URL in a new instance, should leave both instances in the 465*6777b538SAndroid Build Coastguard Worker // background. 466*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app3 = LaunchApplicationSyncExpectSuccess( 467*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {"x-chrome-launch://2"}, 468*6777b538SAndroid Build Coastguard Worker {.create_new_instance = true, .hidden_in_background = true}); 469*6777b538SAndroid Build Coastguard Worker EXPECT_NSNE(app, app3); 470*6777b538SAndroid Build Coastguard Worker EXPECT_NE(app.processIdentifier, app3.processIdentifier); 471*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(3); 472*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(1), @"openURLs"); 473*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(2), @"applicationDidFinishLaunching"); 474*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(2)[@"processIdentifier"], 475*6777b538SAndroid Build Coastguard Worker @(app3.processIdentifier)); 476*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyProhibited); 477*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app2.activationPolicy, NSApplicationActivationPolicyProhibited); 478*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app3.activationPolicy, NSApplicationActivationPolicyProhibited); 479*6777b538SAndroid Build Coastguard Worker} 480*6777b538SAndroid Build Coastguard Worker 481*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, 482*6777b538SAndroid Build Coastguard Worker HiddenInBackground_OpenUrlChangesActivationPolicy) { 483*6777b538SAndroid Build Coastguard Worker std::vector<std::string> command_line_args = {"--test", "--foo"}; 484*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app = LaunchApplicationSyncExpectSuccess( 485*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {}, 486*6777b538SAndroid Build Coastguard Worker {.hidden_in_background = true}); 487*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(app); 488*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(app.bundleIdentifier, helper_bundle_id_); 489*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(helper_app_bundle_path_, apple::NSURLToFilePath(app.bundleURL)); 490*6777b538SAndroid Build Coastguard Worker 491*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(1); 492*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(0), @"applicationDidFinishLaunching"); 493*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(0)[@"activationPolicy"], 494*6777b538SAndroid Build Coastguard Worker @(NSApplicationActivationPolicyProhibited)); 495*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyProhibited); 496*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(0)[@"commandLine"], (@[ 497*6777b538SAndroid Build Coastguard Worker apple::FilePathToNSString(helper_app_executable_path_), 498*6777b538SAndroid Build Coastguard Worker @"--test", @"--foo" 499*6777b538SAndroid Build Coastguard Worker ])); 500*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(0)[@"processIdentifier"], 501*6777b538SAndroid Build Coastguard Worker @(app.processIdentifier)); 502*6777b538SAndroid Build Coastguard Worker 503*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app2 = LaunchApplicationSyncExpectSuccess( 504*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {"chrome://app-launch/0"}, 505*6777b538SAndroid Build Coastguard Worker {.create_new_instance = false, .hidden_in_background = true}); 506*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(app, app2); 507*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app.processIdentifier, app2.processIdentifier); 508*6777b538SAndroid Build Coastguard Worker // Unexpected to me, but opening a URL seems to always change the activation 509*6777b538SAndroid Build Coastguard Worker // policy. 510*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyRegular); 511*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app2.activationPolicy, NSApplicationActivationPolicyRegular); 512*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(3); 513*6777b538SAndroid Build Coastguard Worker EXPECT_THAT( 514*6777b538SAndroid Build Coastguard Worker std::vector<std::string>({SysNSStringToUTF8(LaunchEventName(1)), 515*6777b538SAndroid Build Coastguard Worker SysNSStringToUTF8(LaunchEventName(2))}), 516*6777b538SAndroid Build Coastguard Worker testing::UnorderedElementsAre("activationPolicyChanged", "openURLs")); 517*6777b538SAndroid Build Coastguard Worker} 518*6777b538SAndroid Build Coastguard Worker 519*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, HiddenInBackground_TransitionToForeground) { 520*6777b538SAndroid Build Coastguard Worker std::vector<std::string> command_line_args; 521*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app = LaunchApplicationSyncExpectSuccess( 522*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {"x-chrome-launch://1"}, 523*6777b538SAndroid Build Coastguard Worker {.hidden_in_background = true}); 524*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(app); 525*6777b538SAndroid Build Coastguard Worker 526*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(2); 527*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(0), @"openURLs"); 528*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(1), @"applicationDidFinishLaunching"); 529*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(1)[@"activationPolicy"], 530*6777b538SAndroid Build Coastguard Worker @(NSApplicationActivationPolicyProhibited)); 531*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyProhibited); 532*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(1)[@"processIdentifier"], 533*6777b538SAndroid Build Coastguard Worker @(app.processIdentifier)); 534*6777b538SAndroid Build Coastguard Worker 535*6777b538SAndroid Build Coastguard Worker // Second launch with hidden_in_background set to false should cause the first 536*6777b538SAndroid Build Coastguard Worker // app to switch activation policy. 537*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app2 = LaunchApplicationSyncExpectSuccess( 538*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {}, 539*6777b538SAndroid Build Coastguard Worker {.hidden_in_background = false}); 540*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(app, app2); 541*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(3); 542*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(2), @"activationPolicyChanged"); 543*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(2)[@"activationPolicy"], 544*6777b538SAndroid Build Coastguard Worker @(NSApplicationActivationPolicyRegular)); 545*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app2.activationPolicy, NSApplicationActivationPolicyRegular); 546*6777b538SAndroid Build Coastguard Worker} 547*6777b538SAndroid Build Coastguard Worker 548*6777b538SAndroid Build Coastguard WorkerTEST_F(LaunchApplicationTest, HiddenInBackground_AlreadyInForeground) { 549*6777b538SAndroid Build Coastguard Worker std::vector<std::string> command_line_args; 550*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app = LaunchApplicationSyncExpectSuccess( 551*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {"x-chrome-launch://1"}, 552*6777b538SAndroid Build Coastguard Worker {.hidden_in_background = false}); 553*6777b538SAndroid Build Coastguard Worker ASSERT_TRUE(app); 554*6777b538SAndroid Build Coastguard Worker 555*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(2); 556*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(0), @"openURLs"); 557*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(1), @"applicationDidFinishLaunching"); 558*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(1)[@"activationPolicy"], 559*6777b538SAndroid Build Coastguard Worker @(NSApplicationActivationPolicyRegular)); 560*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app.activationPolicy, NSApplicationActivationPolicyRegular); 561*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(1)[@"processIdentifier"], 562*6777b538SAndroid Build Coastguard Worker @(app.processIdentifier)); 563*6777b538SAndroid Build Coastguard Worker 564*6777b538SAndroid Build Coastguard Worker // Second (and third) launch with hidden_in_background set to true should 565*6777b538SAndroid Build Coastguard Worker // reuse the existing app and keep it visible. 566*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app2 = LaunchApplicationSyncExpectSuccess( 567*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {}, 568*6777b538SAndroid Build Coastguard Worker {.hidden_in_background = true}); 569*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(app, app2); 570*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app2.activationPolicy, NSApplicationActivationPolicyRegular); 571*6777b538SAndroid Build Coastguard Worker NSRunningApplication* app3 = LaunchApplicationSyncExpectSuccess( 572*6777b538SAndroid Build Coastguard Worker helper_app_bundle_path_, command_line_args, {"x-chrome-launch://23"}, 573*6777b538SAndroid Build Coastguard Worker {.hidden_in_background = true}); 574*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(app, app3); 575*6777b538SAndroid Build Coastguard Worker WaitForLaunchEvents(3); 576*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventName(2), @"openURLs"); 577*6777b538SAndroid Build Coastguard Worker EXPECT_NSEQ(LaunchEventData(2)[@"processIdentifier"], 578*6777b538SAndroid Build Coastguard Worker @(app.processIdentifier)); 579*6777b538SAndroid Build Coastguard Worker EXPECT_EQ(app3.activationPolicy, NSApplicationActivationPolicyRegular); 580*6777b538SAndroid Build Coastguard Worker} 581*6777b538SAndroid Build Coastguard Worker 582*6777b538SAndroid Build Coastguard Worker} // namespace 583*6777b538SAndroid Build Coastguard Worker} // namespace base::mac 584