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.
authorPeter Hosey <hg@boredzo.org>
Thu Sep 24 13:22:25 2009 -0700 (2009-09-24)
changeset 44271df5769e87e1
parent 4426 f32e34e30616
child 4428 902fcedc499b
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.
Framework/Source/GrowlApplicationBridge.m
     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], &regItemRef, 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 ? &regItemRef : 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