Switched from using Launch Services to open the .growlRegDict file to sending GHA the open-document event ourselves. This seems to fix the bug where GrowlSafari registering yanks the user out of Safari.
1.1 --- a/Framework/Source/GrowlApplicationBridge.m Thu Sep 24 09:35:42 2009 -0700
1.2 +++ b/Framework/Source/GrowlApplicationBridge.m Thu Sep 24 13:22:25 2009 -0700
1.3 @@ -753,62 +753,97 @@
1.4
1.5 //Houston, we are go for launch.
1.6 if (growlHelperAppPath) {
1.7 - //Let's launch in the background (unfortunately, requires Carbon on Jaguar)
1.8 - LSLaunchFSRefSpec spec;
1.9 - FSRef appRef;
1.10 - OSStatus status = FSPathMakeRef((UInt8 *)[growlHelperAppPath fileSystemRepresentation], &appRef, NULL);
1.11 - if (status == noErr) {
1.12 - FSRef regItemRef;
1.13 - BOOL passRegDict = NO;
1.14 -
1.15 - if (regDict) {
1.16 - OSStatus regStatus;
1.17 - NSString *regDictFileName;
1.18 - NSString *regDictPath;
1.19 -
1.20 - //Obtain a truly unique file name
1.21 - CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
1.22 - CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuid);
1.23 - CFRelease(uuid);
1.24 - regDictFileName = [[NSString stringWithFormat:@"%@-%u-%@", [self _applicationNameForGrowlSearchingRegistrationDictionary:regDict], getpid(), (NSString *)uuidString] stringByAppendingPathExtension:GROWL_REG_DICT_EXTENSION];
1.25 - CFRelease(uuidString);
1.26 - if ([regDictFileName length] > NAME_MAX)
1.27 - regDictFileName = [[regDictFileName substringToIndex:(NAME_MAX - [GROWL_REG_DICT_EXTENSION length])] stringByAppendingPathExtension:GROWL_REG_DICT_EXTENSION];
1.28 -
1.29 - //make sure it's within pathname length constraints
1.30 - regDictPath = [NSTemporaryDirectory() stringByAppendingPathComponent:regDictFileName];
1.31 - if ([regDictPath length] > PATH_MAX)
1.32 - regDictPath = [[regDictPath substringToIndex:(PATH_MAX - [GROWL_REG_DICT_EXTENSION length])] stringByAppendingPathExtension:GROWL_REG_DICT_EXTENSION];
1.33 -
1.34 - //Write the registration dictionary out to the temporary directory
1.35 - NSData *plistData;
1.36 - NSString *error;
1.37 - plistData = [NSPropertyListSerialization dataFromPropertyList:regDict
1.38 - format:NSPropertyListBinaryFormat_v1_0
1.39 - errorDescription:&error];
1.40 - if (plistData) {
1.41 - if (![plistData writeToFile:regDictPath atomically:NO])
1.42 - NSLog(@"GrowlApplicationBridge: Error writing registration dictionary at %@", regDictPath);
1.43 + //Let's launch in the background (requires sending the Apple Event ourselves, as LS may activate the application anyway if it's already running)
1.44 + NSURL *appURL = [NSURL fileURLWithPath:growlHelperAppPath];
1.45 + if (appURL) {
1.46 + OSStatus err;
1.47 +
1.48 + //Find the PSN for GrowlHelperApp. (We'll need this later.)
1.49 + struct ProcessSerialNumber appPSN = {
1.50 + 0, kNoProcess
1.51 + };
1.52 + while ((err = GetNextProcess(&appPSN)) == noErr) {
1.53 + NSDictionary *dict = [(id)ProcessInformationCopyDictionary(&appPSN, kProcessDictionaryIncludeAllInformationMask) autorelease];
1.54 + NSString *bundlePath = [dict objectForKey:@"BundlePath"];
1.55 + if ([bundlePath isEqualToString:growlHelperAppPath]) {
1.56 + //Match!
1.57 + break;
1.58 + }
1.59 + }
1.60 +
1.61 + if (err == noErr) {
1.62 + NSURL *regItemURL = nil;
1.63 + BOOL passRegDict = NO;
1.64 +
1.65 + if (regDict) {
1.66 + NSString *regDictFileName;
1.67 + NSString *regDictPath;
1.68 +
1.69 + //Obtain a truly unique file name
1.70 + CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
1.71 + CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuid);
1.72 + CFRelease(uuid);
1.73 + regDictFileName = [[NSString stringWithFormat:@"%@-%u-%@", [self _applicationNameForGrowlSearchingRegistrationDictionary:regDict], getpid(), (NSString *)uuidString] stringByAppendingPathExtension:GROWL_REG_DICT_EXTENSION];
1.74 + CFRelease(uuidString);
1.75 + if ([regDictFileName length] > NAME_MAX)
1.76 + regDictFileName = [[regDictFileName substringToIndex:(NAME_MAX - [GROWL_REG_DICT_EXTENSION length])] stringByAppendingPathExtension:GROWL_REG_DICT_EXTENSION];
1.77 +
1.78 + //make sure it's within pathname length constraints
1.79 + regDictPath = [NSTemporaryDirectory() stringByAppendingPathComponent:regDictFileName];
1.80 + if ([regDictPath length] > PATH_MAX)
1.81 + regDictPath = [[regDictPath substringToIndex:(PATH_MAX - [GROWL_REG_DICT_EXTENSION length])] stringByAppendingPathExtension:GROWL_REG_DICT_EXTENSION];
1.82 +
1.83 + //Write the registration dictionary out to the temporary directory
1.84 + NSData *plistData;
1.85 + NSString *error;
1.86 + plistData = [NSPropertyListSerialization dataFromPropertyList:regDict
1.87 + format:NSPropertyListBinaryFormat_v1_0
1.88 + errorDescription:&error];
1.89 + if (plistData) {
1.90 + if (![plistData writeToFile:regDictPath atomically:NO])
1.91 + NSLog(@"GrowlApplicationBridge: Error writing registration dictionary at %@", regDictPath);
1.92 + } else {
1.93 + NSLog(@"GrowlApplicationBridge: Error writing registration dictionary at %@: %@", regDictPath, error);
1.94 + NSLog(@"GrowlApplicationBridge: Registration dictionary follows\n%@", regDict);
1.95 + [error release];
1.96 + }
1.97 +
1.98 + if ([[NSFileManager defaultManager] fileExistsAtPath:regDictPath]) {
1.99 + regItemURL = [NSURL fileURLWithPath:regDictPath];
1.100 + passRegDict = YES;
1.101 + }
1.102 + }
1.103 +
1.104 + AEStreamRef stream = AEStreamCreateEvent(kAECoreSuite, kAEOpen,
1.105 + //Target application
1.106 + typeProcessSerialNumber, &appPSN, sizeof(appPSN),
1.107 + kAutoGenerateReturnID, kAnyTransactionID);
1.108 + if (!stream) {
1.109 + NSLog(@"%@: Could not create open-document event to register this application with Growl", [self class]);
1.110 } else {
1.111 - NSLog(@"GrowlApplicationBridge: Error writing registration dictionary at %@: %@", regDictPath, error);
1.112 - NSLog(@"GrowlApplicationBridge: Registration dictionary follows\n%@", regDict);
1.113 - [error release];
1.114 + if (passRegDict) {
1.115 + NSString *regItemURLString = [regItemURL absoluteString];
1.116 + NSData *regItemURLUTF8Data = [regItemURLString dataUsingEncoding:NSUTF8StringEncoding];
1.117 + err = AEStreamWriteKeyDesc(stream, kEventParamDirectObject, typeFileURL, [regItemURLUTF8Data bytes], [regItemURLUTF8Data length]);
1.118 + if (err != noErr) {
1.119 + NSLog(@"%@: Could not set direct object of open-document event to register this application with Growl because AEStreamWriteKeyDesc returned %li/%s", [self class], (long)err, GetMacOSStatusCommentString(err));
1.120 + }
1.121 + }
1.122 +
1.123 + AppleEvent event;
1.124 + err = AEStreamClose(stream, &event);
1.125 + if (err != noErr) {
1.126 + NSLog(@"%@: Could not finish open-document event to register this application with Growl because AEStreamClose returned %li/%s", [self class], (long)err, GetMacOSStatusCommentString(err));
1.127 + } else {
1.128 + err = AESend(&event, /*reply*/ NULL, kAENoReply | kAEDontReconnect | kAENeverInteract | kAEDontRecord, kAENormalPriority, kAEDefaultTimeout, /*idleProc*/ NULL, /*filterProc*/ NULL);
1.129 + if (err != noErr) {
1.130 + NSLog(@"%@: Could not send open-document event to register this application with Growl because AESend returned %li/%s", [self class], (long)err, GetMacOSStatusCommentString(err));
1.131 + }
1.132 + }
1.133 +
1.134 + success = (err == noErr);
1.135 }
1.136 -
1.137 - regStatus = FSPathMakeRef((UInt8 *)[regDictPath fileSystemRepresentation], ®ItemRef, NULL);
1.138 - if (regStatus == noErr)
1.139 - passRegDict = YES;
1.140 }
1.141 -
1.142 - spec.appRef = &appRef;
1.143 - spec.numDocs = (passRegDict != NO);
1.144 - spec.itemRefs = (passRegDict ? ®ItemRef : NULL);
1.145 - spec.passThruParams = NULL;
1.146 - spec.launchFlags = kLSLaunchDontAddToRecents | kLSLaunchDontSwitch | kLSLaunchNoParams | kLSLaunchAsync;
1.147 - spec.asyncRefCon = NULL;
1.148 - status = LSOpenFromRefSpec(&spec, NULL);
1.149 -
1.150 - success = (status == noErr);
1.151 }
1.152 }
1.153