1// Copyright 2023 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// This is a helper application for launch_application_unittest.mm. This 6// application records several events by writing them to a named pipe; 7// the unit tests then use this information to verify that this helper was 8// launched in the correct manner. 9// The named pipe this writes to is equal to the name of the app bundle, 10// with .app replaced by .fifo. 11 12#import <Cocoa/Cocoa.h> 13 14@interface AppDelegate : NSObject <NSApplicationDelegate> 15@end 16 17@implementation AppDelegate { 18 NSArray* _command_line; 19 NSURL* _fifo_url; 20 NSRunningApplication* _running_app; 21} 22 23- (instancetype)initWithCommandLine:(NSArray*)command_line { 24 self = [super init]; 25 if (self) { 26 _command_line = command_line; 27 NSURL* bundle_url = NSBundle.mainBundle.bundleURL; 28 _fifo_url = [bundle_url.URLByDeletingLastPathComponent 29 URLByAppendingPathComponent: 30 [bundle_url.lastPathComponent 31 stringByReplacingOccurrencesOfString:@".app" 32 withString:@".fifo"]]; 33 _running_app = NSRunningApplication.currentApplication; 34 [_running_app addObserver:self 35 forKeyPath:@"activationPolicy" 36 options:NSKeyValueObservingOptionNew 37 context:nil]; 38 } 39 return self; 40} 41 42- (void)dealloc { 43 [_running_app removeObserver:self forKeyPath:@"activationPolicy" context:nil]; 44} 45 46- (void)observeValueForKeyPath:(NSString*)keyPath 47 ofObject:(id)object 48 change:(NSDictionary*)change 49 context:(void*)context { 50 [self 51 addLaunchEvent:@"activationPolicyChanged" 52 withData:@{ 53 @"activationPolicy" : change[@"new"], 54 @"processIdentifier" : 55 @(NSRunningApplication.currentApplication.processIdentifier), 56 }]; 57} 58 59- (void)applicationDidFinishLaunching:(NSNotification*)notification { 60 [self 61 addLaunchEvent:@"applicationDidFinishLaunching" 62 withData:@{ 63 @"activationPolicy" : @(NSApp.activationPolicy), 64 @"commandLine" : _command_line, 65 @"processIdentifier" : 66 @(NSRunningApplication.currentApplication.processIdentifier), 67 }]; 68} 69 70- (void)application:(NSApplication*)app openURLs:(NSArray<NSURL*>*)urls { 71 [app replyToOpenOrPrint:NSApplicationDelegateReplySuccess]; 72 73 NSMutableArray* url_specs = 74 [[NSMutableArray alloc] initWithCapacity:urls.count]; 75 for (NSURL* url in urls) { 76 [url_specs addObject:url.absoluteString]; 77 } 78 [self 79 addLaunchEvent:@"openURLs" 80 withData:@{ 81 @"activationPolicy" : @(NSApp.activationPolicy), 82 @"processIdentifier" : 83 @(NSRunningApplication.currentApplication.processIdentifier), 84 @"urls" : url_specs, 85 }]; 86} 87 88- (void)addLaunchEvent:(NSString*)event { 89 [self addLaunchEvent:event withData:nil]; 90} 91 92- (void)addLaunchEvent:(NSString*)event withData:(NSDictionary*)data { 93 NSLog(@"Logging %@ with data %@", event, data); 94 NSDictionary* event_dict = @{ 95 @"name" : event, 96 @"data" : data, 97 }; 98 // It is important to write this dictionary to the named pipe non-atomically, 99 // as otherwise the write would replace the named pipe with a regular file 100 // rather than writing to the pipe. 101 NSData* plist_data = [NSPropertyListSerialization 102 dataWithPropertyList:event_dict 103 format:NSPropertyListXMLFormat_v1_0 104 options:0 105 error:nil]; 106 [plist_data writeToURL:_fifo_url options:0 error:nil]; 107} 108 109@end 110 111__attribute__((visibility("default"))) int main(int argc, char** argv) { 112 [NSApplication sharedApplication]; 113 114 NSMutableArray* command_line = [[NSMutableArray alloc] initWithCapacity:argc]; 115 for (int i = 0; i < argc; ++i) { 116 [command_line addObject:[NSString stringWithUTF8String:argv[i]]]; 117 } 118 119 AppDelegate* delegate = 120 [[AppDelegate alloc] initWithCommandLine:command_line]; 121 NSApp.delegate = delegate; 122 123 [NSApp run]; 124 return 0; 125} 126