// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif
// This is a helper application for launch_application_unittest.mm. This
// application records several events by writing them to a named pipe;
// the unit tests then use this information to verify that this helper was
// launched in the correct manner.
// The named pipe this writes to is equal to the name of the app bundle,
// with .app replaced by .fifo.
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
@end
@implementation AppDelegate {
NSArray* _command_line;
NSURL* _fifo_url;
NSRunningApplication* _running_app;
}
- (instancetype)initWithCommandLine:(NSArray*)command_line {
self = [super init];
if (self) {
_command_line = command_line;
NSURL* bundle_url = NSBundle.mainBundle.bundleURL;
_fifo_url = [bundle_url.URLByDeletingLastPathComponent
URLByAppendingPathComponent:
[bundle_url.lastPathComponent
stringByReplacingOccurrencesOfString:@".app"
withString:@".fifo"]];
_running_app = NSRunningApplication.currentApplication;
[_running_app addObserver:self
forKeyPath:@"activationPolicy"
options:NSKeyValueObservingOptionNew
context:nil];
}
return self;
}
- (void)dealloc {
[_running_app removeObserver:self forKeyPath:@"activationPolicy" context:nil];
}
- (void)observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context {
[self
addLaunchEvent:@"activationPolicyChanged"
withData:@{
@"activationPolicy" : change[@"new"],
@"processIdentifier" :
@(NSRunningApplication.currentApplication.processIdentifier),
}];
}
- (void)applicationDidFinishLaunching:(NSNotification*)notification {
[self
addLaunchEvent:@"applicationDidFinishLaunching"
withData:@{
@"activationPolicy" : @(NSApp.activationPolicy),
@"commandLine" : _command_line,
@"processIdentifier" :
@(NSRunningApplication.currentApplication.processIdentifier),
}];
}
- (void)application:(NSApplication*)app openURLs:(NSArray<NSURL*>*)urls {
[app replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
NSMutableArray* url_specs =
[[NSMutableArray alloc] initWithCapacity:urls.count];
for (NSURL* url in urls) {
[url_specs addObject:url.absoluteString];
}
[self
addLaunchEvent:@"openURLs"
withData:@{
@"activationPolicy" : @(NSApp.activationPolicy),
@"processIdentifier" :
@(NSRunningApplication.currentApplication.processIdentifier),
@"urls" : url_specs,
}];
}
- (void)addLaunchEvent:(NSString*)event {
[self addLaunchEvent:event withData:nil];
}
- (void)addLaunchEvent:(NSString*)event withData:(NSDictionary*)data {
NSLog(@"Logging %@ with data %@", event, data);
NSDictionary* event_dict = @{
@"name" : event,
@"data" : data,
};
// It is important to write this dictionary to the named pipe non-atomically,
// as otherwise the write would replace the named pipe with a regular file
// rather than writing to the pipe.
NSData* plist_data = [NSPropertyListSerialization
dataWithPropertyList:event_dict
format:NSPropertyListXMLFormat_v1_0
options:0
error:nil];
[plist_data writeToURL:_fifo_url options:0 error:nil];
}
@end
__attribute__((visibility("default"))) int main(int argc, char** argv) {
[NSApplication sharedApplication];
NSMutableArray* command_line = [[NSMutableArray alloc] initWithCapacity:argc];
for (int i = 0; i < argc; ++i) {
[command_line addObject:[NSString stringWithUTF8String:argv[i]]];
}
AppDelegate* delegate =
[[AppDelegate alloc] initWithCommandLine:command_line];
NSApp.delegate = delegate;
[NSApp run];
return 0;
}