Bug 827979: Add Growl support back to Aurora. a=akeybl
authorJosh Aas <joshmoz@gmail.com>
Fri, 25 Jan 2013 15:45:45 -0500
changeset 127314 95a748d5cf96e4be38fd27c5e3400e4a022e6334
parent 127313 55237706be0b24d03cb46a5850c4ca3d7ce302b6
child 127315 74eae9019c9a1f12f774b74face98f66606da609
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersakeybl
bugs827979
milestone20.0a2
Bug 827979: Add Growl support back to Aurora. a=akeybl
toolkit/components/alerts/Makefile.in
toolkit/components/alerts/mac/Makefile.in
toolkit/components/alerts/mac/growl/CFGrowlAdditions.c
toolkit/components/alerts/mac/growl/CFGrowlAdditions.h
toolkit/components/alerts/mac/growl/CFGrowlDefines.h
toolkit/components/alerts/mac/growl/CFMutableDictionaryAdditions.c
toolkit/components/alerts/mac/growl/CFMutableDictionaryAdditions.h
toolkit/components/alerts/mac/growl/CFURLAdditions.c
toolkit/components/alerts/mac/growl/CFURLAdditions.h
toolkit/components/alerts/mac/growl/GrowlAbstractSingletonObject.h
toolkit/components/alerts/mac/growl/GrowlApplicationBridge.h
toolkit/components/alerts/mac/growl/GrowlApplicationBridge.m
toolkit/components/alerts/mac/growl/GrowlDefines.h
toolkit/components/alerts/mac/growl/GrowlDefinesInternal.h
toolkit/components/alerts/mac/growl/GrowlPathUtilities.h
toolkit/components/alerts/mac/growl/GrowlPathUtilities.m
toolkit/components/alerts/mac/growl/GrowlPathway.h
toolkit/components/alerts/mac/growl/GrowlPreferencesController.h
toolkit/components/alerts/mac/growl/GrowlTicketController.h
toolkit/components/alerts/mac/growl/Makefile.in
toolkit/components/alerts/mac/growl/license.txt
toolkit/components/alerts/mac/mozGrowlDelegate.h
toolkit/components/alerts/mac/mozGrowlDelegate.mm
toolkit/components/alerts/mac/nsAlertsImageLoadListener.h
toolkit/components/alerts/mac/nsAlertsImageLoadListener.mm
toolkit/components/alerts/mac/nsMacAlertsService.h
toolkit/components/alerts/mac/nsMacAlertsService.mm
toolkit/components/alerts/mac/nsNotificationsList.h
toolkit/components/alerts/mac/nsNotificationsList.mm
toolkit/components/alerts/nsINotificationsList.idl
toolkit/components/alerts/test/Makefile.in
toolkit/content/license.html
toolkit/toolkit-makefiles.sh
--- a/toolkit/components/alerts/Makefile.in
+++ b/toolkit/components/alerts/Makefile.in
@@ -17,17 +17,20 @@ LIBRARY_NAME = alerts_s
 FORCE_STATIC_LIB = 1
 LIBXUL_LIBRARY = 1
 CPPSRCS = \
   nsAlertsService.cpp \
   $(NULL)
 LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/components/build/
 
 ifneq (,$(filter cocoa, $(MOZ_WIDGET_TOOLKIT)))
+XPIDLSRCS += nsINotificationsList.idl
+# mac/growl needs to be first for linking to work!
 TOOL_DIRS += \
+  mac/growl \
   mac \
   $(NULL)
 endif
 
 TEST_DIRS += test
 
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
--- a/toolkit/components/alerts/mac/Makefile.in
+++ b/toolkit/components/alerts/mac/Makefile.in
@@ -11,32 +11,40 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE = alerts
 LIBRARY_NAME = alerts
 IS_COMPONENT = 1
 FORCE_SHARED_LIB = 1
 
 CMMSRCS = \
   nsMacAlertsService.mm \
+  mozGrowlDelegate.mm \
   mozNotificationCenterDelegate.mm \
+  nsAlertsImageLoadListener.mm \
+  nsNotificationsList.mm \
   ObserverPair.mm \
   $(NULL)
 
 CPPSRCS = \
   nsAlertsServiceModule.cpp \
   $(NULL)
 
 LOCAL_INCLUDES += \
+  -I$(srcdir)/growl/ \
   -I$(topsrcdir)/toolkit/components/build/ \
   $(NULL)
 
 EXPORTS = \
   nsMacAlertsService.h \
   $(NULL)
 
+SHARED_LIBRARY_LIBS = \
+  growl/$(LIB_PREFIX)growl_s.$(LIB_SUFFIX) \
+  $(NULL)
+
 ifdef JS_SHARED_LIBRARY
 js_ldopts = $(MOZ_JS_LIBS)
 else
 js_ldopts = $(LIBXUL_LIBS)
 endif
 
 EXTRA_DSO_LDOPTS += \
   -framework Carbon \
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/CFGrowlAdditions.c
@@ -0,0 +1,629 @@
+//
+//  CFGrowlAdditions.c
+//  Growl
+//
+//  Created by Peter Hosey on Wed Jun 18 2004.
+//  Copyright 2005-2006 The Growl Project.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+#include <Carbon/Carbon.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include "CFGrowlAdditions.h"
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+extern Boolean CFStringGetFileSystemRepresentation() __attribute__((weak_import));
+extern CFIndex CFStringGetMaximumSizeOfFileSystemRepresentation(CFStringRef string) __attribute__((weak_import));
+
+char *createFileSystemRepresentationOfString(CFStringRef str) {
+	char *buffer;
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4
+	/* CFStringGetFileSystemRepresentation will cause a link error despite the weak_import attribute above on 10.5 when compiling with 10.2 compatibility using gcc 3.3.
+	 * PPC will therefore always use the 10.3 and below method of creating a file system representation.
+	 */
+	if (CFStringGetFileSystemRepresentation) {
+		CFIndex size = CFStringGetMaximumSizeOfFileSystemRepresentation(str);
+		buffer = malloc(size);
+		CFStringGetFileSystemRepresentation(str, buffer, size);
+	} else 
+#endif
+	{
+		buffer = malloc(512);
+		CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, str, kCFURLPOSIXPathStyle, false);
+		if (!CFURLGetFileSystemRepresentation(url, false, (UInt8 *)buffer, 512)) {
+			free(buffer);
+			buffer = NULL;
+		}
+		CFRelease(url);
+	}
+	return buffer;
+}
+
+STRING_TYPE createStringWithDate(CFDateRef date) {
+	CFLocaleRef locale = CFLocaleCopyCurrent();
+	CFDateFormatterRef dateFormatter = CFDateFormatterCreate(kCFAllocatorDefault,
+															 locale,
+															 kCFDateFormatterMediumStyle,
+															 kCFDateFormatterMediumStyle);
+	CFRelease(locale);
+	CFStringRef dateString = CFDateFormatterCreateStringWithDate(kCFAllocatorDefault,
+																 dateFormatter,
+																 date);
+	CFRelease(dateFormatter);
+	return dateString;
+}
+
+STRING_TYPE createStringWithContentsOfFile(CFStringRef filename, CFStringEncoding encoding) {
+	CFStringRef str = NULL;
+
+	char *path = createFileSystemRepresentationOfString(filename);
+	if (path) {
+		FILE *fp = fopen(path, "rb");
+		if (fp) {
+			fseek(fp, 0, SEEK_END);
+			unsigned long size = ftell(fp);
+			fseek(fp, 0, SEEK_SET);
+			unsigned char *buffer = malloc(size);
+			if (buffer && fread(buffer, 1, size, fp) == size)
+				str = CFStringCreateWithBytes(kCFAllocatorDefault, buffer, size, encoding, true);
+			fclose(fp);
+		}
+		free(path);
+	}
+
+	return str;
+}
+
+STRING_TYPE createStringWithStringAndCharacterAndString(STRING_TYPE str0, UniChar ch, STRING_TYPE str1) {
+	CFStringRef cfstr0 = (CFStringRef)str0;
+	CFStringRef cfstr1 = (CFStringRef)str1;
+	CFIndex len0 = (cfstr0 ? CFStringGetLength(cfstr0) : 0);
+	CFIndex len1 = (cfstr1 ? CFStringGetLength(cfstr1) : 0);
+	size_t length = (len0 + (ch != 0xffff) + len1);
+
+	UniChar *buf = malloc(sizeof(UniChar) * length);
+	size_t i = 0U;
+
+	if (cfstr0) {
+		CFStringGetCharacters(cfstr0, CFRangeMake(0, len0), buf);
+		i += len0;
+	}
+	if (ch != 0xffff)
+		buf[i++] = ch;
+	if (cfstr1)
+		CFStringGetCharacters(cfstr1, CFRangeMake(0, len1), &buf[i]);
+
+	return CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, buf, length, /*contentsDeallocator*/ kCFAllocatorMalloc);
+}
+
+char *copyCString(STRING_TYPE str, CFStringEncoding encoding) {
+	CFIndex size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), encoding) + 1;
+	char *buffer = calloc(size, 1);
+	CFStringGetCString(str, buffer, size, encoding);
+	return buffer;
+}
+
+STRING_TYPE copyCurrentProcessName(void) {
+	ProcessSerialNumber PSN = { 0, kCurrentProcess };
+	CFStringRef name = NULL;
+	OSStatus err = CopyProcessName(&PSN, &name);
+	if (err != noErr) {
+		NSLog(CFSTR("in copyCurrentProcessName in CFGrowlAdditions: Could not get process name because CopyProcessName returned %li"), (long)err);
+		name = NULL;
+	}
+	return name;
+}
+
+URL_TYPE copyCurrentProcessURL(void) {
+	ProcessSerialNumber psn = { 0, kCurrentProcess };
+	FSRef fsref;
+	CFURLRef URL = NULL;
+	OSStatus err = GetProcessBundleLocation(&psn, &fsref);
+	if (err != noErr) {
+		NSLog(CFSTR("in copyCurrentProcessURL in CFGrowlAdditions: Could not get application location, because GetProcessBundleLocation returned %li\n"), (long)err);
+	} else {
+		URL = CFURLCreateFromFSRef(kCFAllocatorDefault, &fsref);
+	}
+	return URL;
+}
+STRING_TYPE copyCurrentProcessPath(void) {
+	CFURLRef URL = copyCurrentProcessURL();
+	CFStringRef path = CFURLCopyFileSystemPath(URL, kCFURLPOSIXPathStyle);
+	CFRelease(URL);
+	return path;
+}
+
+URL_TYPE copyTemporaryFolderURL(void) {
+	FSRef ref;
+	CFURLRef url = NULL;
+
+	OSStatus err = FSFindFolder(kOnAppropriateDisk, kTemporaryFolderType, kCreateFolder, &ref);
+	if (err != noErr)
+		NSLog(CFSTR("in copyTemporaryFolderPath in CFGrowlAdditions: Could not locate temporary folder because FSFindFolder returned %li"), (long)err);
+	else
+		url = CFURLCreateFromFSRef(kCFAllocatorDefault, &ref);
+
+	return url;
+}
+STRING_TYPE copyTemporaryFolderPath(void) {
+	CFStringRef path = NULL;
+
+	CFURLRef url = copyTemporaryFolderURL();
+	if (url) {
+		path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
+		CFRelease(url);
+	}
+
+	return path;
+}
+
+DATA_TYPE readFile(const char *filename)
+{
+	CFDataRef data;
+	// read the file into a CFDataRef
+	FILE *fp = fopen(filename, "r");
+	if (fp) {
+		fseek(fp, 0, SEEK_END);
+		long dataLength = ftell(fp);
+		fseek(fp, 0, SEEK_SET);
+		unsigned char *fileData = malloc(dataLength);
+		fread(fileData, 1, dataLength, fp);
+		fclose(fp);
+		data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, fileData, dataLength, kCFAllocatorMalloc);
+	} else
+		data = NULL;
+
+	return data;
+}
+
+URL_TYPE copyURLForApplication(STRING_TYPE appName)
+{
+	CFURLRef appURL = NULL;
+	OSStatus err = LSFindApplicationForInfo(/*inCreator*/  kLSUnknownCreator,
+											/*inBundleID*/ NULL,
+											/*inName*/     appName,
+											/*outAppRef*/  NULL,
+											/*outAppURL*/  &appURL);
+	return (err == noErr) ? appURL : NULL;
+}
+
+STRING_TYPE createStringWithAddressData(DATA_TYPE aAddressData) {
+	struct sockaddr *socketAddress = (struct sockaddr *)CFDataGetBytePtr(aAddressData);
+	// IPv6 Addresses are "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"
+	//      at max, which is 40 bytes (0-terminated)
+	// IPv4 Addresses are "255.255.255.255" at max which is smaller
+	char stringBuffer[40];
+	CFStringRef addressAsString = NULL;
+	if (socketAddress->sa_family == AF_INET) {
+		struct sockaddr_in *ipv4 = (struct sockaddr_in *)socketAddress;
+		if (inet_ntop(AF_INET, &(ipv4->sin_addr), stringBuffer, 40))
+			addressAsString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s:%d"), stringBuffer, ipv4->sin_port);
+		else
+			addressAsString = CFSTR("IPv4 un-ntopable");
+	} else if (socketAddress->sa_family == AF_INET6) {
+		struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)socketAddress;
+		if (inet_ntop(AF_INET6, &(ipv6->sin6_addr), stringBuffer, 40))
+			// Suggested IPv6 format (see http://www.faqs.org/rfcs/rfc2732.html)
+			addressAsString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("[%s]:%d"), stringBuffer, ipv6->sin6_port);
+		else
+			addressAsString = CFSTR("IPv6 un-ntopable");
+	} else
+		addressAsString = CFSTR("neither IPv6 nor IPv4");
+
+	return addressAsString;
+}
+
+STRING_TYPE createHostNameForAddressData(DATA_TYPE aAddressData) {
+	char hostname[NI_MAXHOST];
+	struct sockaddr *socketAddress = (struct sockaddr *)CFDataGetBytePtr(aAddressData);
+	if (getnameinfo(socketAddress, (socklen_t)CFDataGetLength(aAddressData),
+					hostname, (socklen_t)sizeof(hostname),
+					/*serv*/ NULL, /*servlen*/ 0,
+					NI_NAMEREQD))
+		return NULL;
+	else
+		return CFStringCreateWithCString(kCFAllocatorDefault, hostname, kCFStringEncodingASCII);
+}
+
+DATA_TYPE copyIconDataForPath(STRING_TYPE path) {
+	CFDataRef data = NULL;
+
+	//false is probably safest, and is harmless when the object really is a directory.
+	CFURLRef URL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, /*isDirectory*/ false);
+	if (URL) {
+		data = copyIconDataForURL(URL);
+		CFRelease(URL);
+	}
+
+	return data;
+}
+
+DATA_TYPE copyIconDataForURL(URL_TYPE URL)
+{
+	CFDataRef data = NULL;
+
+	if (URL) {
+		FSRef ref;
+		if (CFURLGetFSRef(URL, &ref)) {
+			IconRef icon = NULL;
+			SInt16 label_noOneCares;
+			OSStatus err = GetIconRefFromFileInfo(&ref,
+												  /*inFileNameLength*/ 0U, /*inFileName*/ NULL,
+												  kFSCatInfoNone, /*inCatalogInfo*/ NULL,
+												  kIconServicesNoBadgeFlag | kIconServicesUpdateIfNeededFlag,
+												  &icon,
+												  &label_noOneCares);
+			if (err != noErr) {
+				NSLog(CFSTR("in copyIconDataForURL in CFGrowlAdditions: could not get icon for %@: GetIconRefFromFileInfo returned %li\n"), URL, (long)err);
+			} else {
+				IconFamilyHandle fam = NULL;
+				err = IconRefToIconFamily(icon, kSelectorAllAvailableData, &fam);
+				if (err != noErr) {
+					NSLog(CFSTR("in copyIconDataForURL in CFGrowlAdditions: could not get icon for %@: IconRefToIconFamily returned %li\n"), URL, (long)err);
+				} else {
+					HLock((Handle)fam);
+					data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)*(Handle)fam, GetHandleSize((Handle)fam));
+					HUnlock((Handle)fam);
+					DisposeHandle((Handle)fam);
+				}
+				ReleaseIconRef(icon);
+			}
+		}
+	}
+
+	return data;
+}
+
+URL_TYPE createURLByMakingDirectoryAtURLWithName(URL_TYPE parent, STRING_TYPE name)
+{
+	CFURLRef newDirectory = NULL;
+
+	CFAllocatorRef allocator = parent ? CFGetAllocator(parent) : name ? CFGetAllocator(name) : kCFAllocatorDefault;
+
+	if (parent) parent = CFRetain(parent);
+	else {
+		char *cwdBytes = alloca(PATH_MAX);
+		getcwd(cwdBytes, PATH_MAX);
+		parent = CFURLCreateFromFileSystemRepresentation(allocator, (const unsigned char *)cwdBytes, strlen(cwdBytes), /*isDirectory*/ true);
+		if (!name) {
+			newDirectory = parent;
+			goto end;
+		}
+	}
+	if (!parent)
+		NSLog(CFSTR("in createURLByMakingDirectoryAtURLWithName in CFGrowlAdditions: parent directory URL is NULL (please tell the Growl developers)\n"), parent);
+	else {
+		if (name)
+			name = CFRetain(name);
+		else {
+			name = CFURLCopyLastPathComponent(parent);
+			CFURLRef newParent = CFURLCreateCopyDeletingLastPathComponent(allocator, parent);
+			CFRelease(parent);
+			parent = newParent;
+		}
+
+		if (!name)
+			NSLog(CFSTR("in createURLByMakingDirectoryAtURLWithName in CFGrowlAdditions: name of directory to create is NULL (please tell the Growl developers)\n"), parent);
+		else {
+			FSRef parentRef;
+			if (!CFURLGetFSRef(parent, &parentRef))
+				NSLog(CFSTR("in createURLByMakingDirectoryAtURLWithName in CFGrowlAdditions: could not create FSRef for parent directory at %@ (please tell the Growl developers)\n"), parent);
+			else {
+				FSRef newDirectoryRef;
+
+				struct HFSUniStr255 nameUnicode;
+				CFRange range = { 0, MIN(CFStringGetLength(name), USHRT_MAX) };
+				CFStringGetCharacters(name, range, nameUnicode.unicode);
+				nameUnicode.length = range.length;
+
+				struct FSRefParam refPB = {
+					.ref              = &parentRef,
+					.nameLength       = nameUnicode.length,
+					.name             = nameUnicode.unicode,
+					.whichInfo        = kFSCatInfoNone,
+					.catInfo          = NULL,
+					.textEncodingHint = kTextEncodingUnknown,
+					.newRef           = &newDirectoryRef,
+				};
+
+				OSStatus err = PBCreateDirectoryUnicodeSync(&refPB);
+				if (err == dupFNErr) {
+					//dupFNErr == file (or folder) exists already. this is fine.
+					err = PBMakeFSRefUnicodeSync(&refPB);
+				}
+				if (err == noErr) {
+					NSLog(CFSTR("PBCreateDirectoryUnicodeSync or PBMakeFSRefUnicodeSync returned %li; calling CFURLCreateFromFSRef"), (long)err); //XXX
+					newDirectory = CFURLCreateFromFSRef(allocator, &newDirectoryRef);
+					NSLog(CFSTR("CFURLCreateFromFSRef returned %@"), newDirectory); //XXX
+				} else
+					NSLog(CFSTR("in createURLByMakingDirectoryAtURLWithName in CFGrowlAdditions: could not create directory '%@' in parent directory at %@: FSCreateDirectoryUnicode returned %li (please tell the Growl developers)"), name, parent, (long)err);
+			}
+
+		} //if (name)
+		if(parent)
+			CFRelease(parent);
+		if(name)
+			CFRelease(name);
+	} //if (parent)
+
+end:
+	return newDirectory;
+}
+
+#ifndef COPYFORK_BUFSIZE
+#	define COPYFORK_BUFSIZE 5242880U /*5 MiB*/
+#endif
+
+static OSStatus copyFork(const struct HFSUniStr255 *forkName, const FSRef *srcFile, const FSRef *destDir, const struct HFSUniStr255 *destName, FSRef *outDestFile) {
+	OSStatus err, closeErr;
+	struct FSForkIOParam srcPB = {
+		.ref = srcFile,
+		.forkNameLength = forkName->length,
+		.forkName = forkName->unicode,
+		.permissions = fsRdPerm,
+	};
+	unsigned char debuggingPathBuf[PATH_MAX] = "";
+	OSStatus debuggingPathErr;
+
+	err = PBOpenForkSync(&srcPB);
+	if (err != noErr) {
+		debuggingPathErr = FSRefMakePath(srcFile, debuggingPathBuf, PATH_MAX);
+		if (debuggingPathErr != noErr)
+			snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for source file: FSRefMakePath returned %li)", (long)debuggingPathErr);
+		NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBOpenForkSync (source: %s) returned %li"), debuggingPathBuf, (long)err);
+	} else {
+		FSRef destFile;
+
+		/*the first thing to do is get the name of the destination file, if one
+		 *	wasn't provided.
+		 *and while we're at it, we get the catalogue info as well.
+		 */
+		struct FSCatalogInfo catInfo;
+		struct FSRefParam refPB = {
+			.ref       = srcFile,
+			.whichInfo = kFSCatInfoGettableInfo & kFSCatInfoSettableInfo,
+			.catInfo   = &catInfo,
+			.spec      = NULL,
+			.parentRef = NULL,
+			.outName   = destName ? NULL : (struct HFSUniStr255 *)(destName = alloca(sizeof(struct HFSUniStr255))),
+		};
+
+		err = PBGetCatalogInfoSync(&refPB);
+		if (err != noErr) {
+			debuggingPathErr = FSRefMakePath(srcFile, debuggingPathBuf, PATH_MAX);
+			if (debuggingPathErr != noErr)
+				snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for source file: FSRefMakePath returned %li)", (long)debuggingPathErr);
+			NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBGetCatalogInfoSync (source: %s) returned %li"), debuggingPathBuf, (long)err);
+		} else {
+			refPB.ref              = destDir;
+			refPB.nameLength       = destName->length;
+			refPB.name             = destName->unicode;
+			refPB.textEncodingHint = kTextEncodingUnknown;
+			refPB.newRef           = &destFile;
+
+			const char *functionName = "PBMakeFSRefUnicodeSync"; //for error-reporting message
+
+			err = PBMakeFSRefUnicodeSync(&refPB);
+			if ((err != noErr) && (err != fnfErr)) {
+			handleMakeFSRefError:
+				debuggingPathErr = FSRefMakePath(destDir, debuggingPathBuf, PATH_MAX);
+				if (debuggingPathErr != noErr)
+					snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for destination directory: FSRefMakePath returned %li)", (long)debuggingPathErr);
+
+				//get filename too
+				CFStringRef debuggingFilename = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
+																				   destName->unicode,
+																				   destName->length,
+																				   /*contentsDeallocator*/ kCFAllocatorNull);
+				if (!debuggingFilename)
+					debuggingFilename = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, "(could not get filename for destination file: CFStringCreateWithCharactersNoCopy returned NULL)", kCFStringEncodingASCII, /*contentsDeallocator*/ kCFAllocatorNull);
+
+				NSLog(CFSTR("in copyFork in CFGrowlAdditions: %s (destination: %s/%@) returned %li"), functionName, debuggingPathBuf, debuggingFilename, (long)err);
+
+				if (debuggingFilename) CFRelease(debuggingFilename);
+			} else {
+				//that file doesn't exist in that folder; create it.
+				err = PBCreateFileUnicodeSync(&refPB);
+				if (err == noErr) {
+					/*make sure the Finder knows about the new file.
+					 *FNNotify returns a status code too, but this isn't an
+					 *	essential step, so we just ignore it.
+					 */
+					FNNotify(destDir, kFNDirectoryModifiedMessage, kNilOptions);
+				} else if (err == dupFNErr) {
+					/*dupFNErr: the file already exists.
+					 *we can safely ignore this error.
+					 */
+					err = noErr;
+				} else {
+					functionName = "PBCreateFileUnicodeSync";
+					goto handleMakeFSRefError;
+				}
+			}
+		}
+		if (err == noErr) {
+			if (outDestFile)
+				memcpy(outDestFile, &destFile, sizeof(destFile));
+
+			struct FSForkIOParam destPB = {
+				.ref            = &destFile,
+				.forkNameLength = forkName->length,
+				.forkName       = forkName->unicode,
+				.permissions    = fsWrPerm,
+			};
+			err = PBOpenForkSync(&destPB);
+			NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBOpenForkSync (dest) returned %li"), (long)err);
+			if (err != noErr) {
+				debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
+				if (debuggingPathErr != noErr)
+					snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for dest file: FSRefMakePath returned %li)", (long)debuggingPathErr);
+				NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBOpenForkSync (destination: %s) returned %li"), debuggingPathBuf, (long)err);
+			} else {
+				void *buf = malloc(COPYFORK_BUFSIZE);
+				if (buf) {
+					srcPB.buffer = destPB.buffer = buf;
+					srcPB.requestCount = COPYFORK_BUFSIZE;
+					while (err == noErr) {
+						err = PBReadForkSync(&srcPB);
+						if (err == eofErr) {
+							err = noErr;
+							if (srcPB.actualCount == 0)
+								break;
+						}
+						if (err != noErr) {
+							debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
+							if (debuggingPathErr != noErr)
+								snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for source file: FSRefMakePath returned %li)", (long)debuggingPathErr);
+							NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBReadForkSync (source: %s) returned %li"), debuggingPathBuf, (long)err);
+						} else {
+							destPB.requestCount = srcPB.actualCount;
+							err = PBWriteForkSync(&destPB);
+							if (err != noErr) {
+								debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
+								if (debuggingPathErr != noErr)
+									snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for dest file: FSRefMakePath returned %li)", (long)debuggingPathErr);
+								NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBWriteForkSync (destination: %s) returned %li"), debuggingPathBuf, (long)err);
+							}
+						}
+					}
+
+					free(buf);
+				}
+
+				closeErr = PBCloseForkSync(&destPB);
+				if (closeErr != noErr) {
+					debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
+					if (debuggingPathErr != noErr)
+						snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for dest file: FSRefMakePath returned %li)", (long)debuggingPathErr);
+					NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBCloseForkSync (destination: %s) returned %li"), debuggingPathBuf, (long)err);
+				}
+				if (err == noErr) err = closeErr;
+			}
+		}
+
+		closeErr = PBCloseForkSync(&srcPB);
+		if (closeErr != noErr) {
+			debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
+			if (debuggingPathErr != noErr)
+				snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for source file: FSRefMakePath returned %li)", (long)debuggingPathErr);
+			NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBCloseForkSync (source: %s) returned %li"), debuggingPathBuf, (long)err);
+		}
+		if (err == noErr) err = closeErr;
+	}
+
+	return err;
+}
+
+static OSStatus GrowlCopyObjectSync(const FSRef *fileRef, const FSRef *destRef, FSRef *destFileRef) {
+	OSStatus err;
+	struct HFSUniStr255 forkName;
+	struct FSForkIOParam forkPB = {
+		.ref = fileRef,
+		.forkIterator = {
+			.initialize = 0
+		},
+		.outForkName = &forkName,
+	};
+
+	do {
+		err = PBIterateForksSync(&forkPB);
+		NSLog(CFSTR("PBIterateForksSync returned %li"), (long)err);
+		if (err != noErr) {
+			if (err != errFSNoMoreItems)
+				NSLog(CFSTR("in GrowlCopyObjectSync in CFGrowlAdditions: PBIterateForksSync returned %li"), (long)err);
+		} else {
+			err = copyFork(&forkName, fileRef, destRef, /*destName*/ NULL, /*outDestFile*/ destFileRef);
+			//copyFork prints its own error messages
+		}
+	} while (err == noErr);
+	if (err == errFSNoMoreItems) err = noErr;
+
+	return err;
+}
+
+URL_TYPE createURLByCopyingFileFromURLToDirectoryURL(URL_TYPE file, URL_TYPE dest)
+{
+	CFURLRef destFileURL = NULL;
+
+	FSRef fileRef, destRef, destFileRef;
+	Boolean gotFileRef = CFURLGetFSRef(file, &fileRef);
+	Boolean gotDestRef = CFURLGetFSRef(dest, &destRef);
+	if (!gotFileRef)
+		NSLog(CFSTR("in createURLByCopyingFileFromURLToDirectoryURL in CFGrowlAdditions: CFURLGetFSRef failed with source URL %@"), file);
+	else if (!gotDestRef)
+		NSLog(CFSTR("in createURLByCopyingFileFromURLToDirectoryURL in CFGrowlAdditions: CFURLGetFSRef failed with destination URL %@"), dest);
+	else {
+		OSStatus err;
+
+		/*
+		 * 10.2 has a problem with weak symbols in frameworks so we use
+		 * MAC_OS_X_VERSION_MIN_REQUIRED >= 10.3.
+		 */
+#if defined(NSAppKitVersionNumber10_3) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3
+		if (FSCopyObjectSync) {
+			err = FSCopyObjectSync(&fileRef, &destRef, /*destName*/ NULL, &destFileRef, kFSFileOperationOverwrite);
+		} else {
+#endif
+			err = GrowlCopyObjectSync(&fileRef, &destRef, &destFileRef);
+#if defined(NSAppKitVersionNumber10_3) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3
+		}
+#endif
+
+		if (err == noErr)
+			destFileURL = CFURLCreateFromFSRef(kCFAllocatorDefault, &destFileRef);
+		else
+			NSLog(CFSTR("in createURLByCopyingFileFromURLToDirectoryURL in CFGrowlAdditions: CopyObjectSync returned %li for source URL %@"), (long)err, file);
+	}
+
+	return destFileURL;
+}
+
+PLIST_TYPE createPropertyListFromURL(URL_TYPE file, u_int32_t mutability, CFPropertyListFormat *outFormat, STRING_TYPE *outErrorString)
+{
+	CFPropertyListRef plist = NULL;
+
+	if (!file)
+		NSLog(CFSTR("in createPropertyListFromURL in CFGrowlAdditions: cannot read from a NULL URL"));
+	else {
+		CFReadStreamRef stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, file);
+		if (!stream)
+			NSLog(CFSTR("in createPropertyListFromURL in CFGrowlAdditions: could not create stream for reading from URL %@"), file);
+		else {
+			if (!CFReadStreamOpen(stream))
+				NSLog(CFSTR("in createPropertyListFromURL in CFGrowlAdditions: could not open stream for reading from URL %@"), file);
+			else {
+				CFPropertyListFormat format;
+				CFStringRef errorString = NULL;
+
+				plist = CFPropertyListCreateFromStream(kCFAllocatorDefault,
+													   stream,
+													   /*streamLength*/ 0,
+													   mutability,
+													   &format,
+													   &errorString);
+				if (!plist)
+					NSLog(CFSTR("in createPropertyListFromURL in CFGrowlAdditions: could not read property list from URL %@ (error string: %@)"), file, errorString);
+
+				if (outFormat) *outFormat = format;
+				if (errorString) {
+					if (outErrorString)
+						*outErrorString = errorString;
+					else
+						CFRelease(errorString);
+				}
+
+				CFReadStreamClose(stream);
+			}
+
+			CFRelease(stream);
+		}
+	}
+
+	return plist;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/CFGrowlAdditions.h
@@ -0,0 +1,86 @@
+//
+//  CFGrowlAdditions.h
+//  Growl
+//
+//  Created by Peter Hosey on Wed Jun 18 2004.
+//  Copyright 2005-2006 The Growl Project.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+#ifndef HAVE_CFGROWLADDITIONS_H
+#define HAVE_CFGROWLADDITIONS_H
+
+#include "CFGrowlDefines.h"
+
+//see GrowlApplicationBridge-Carbon.c for rationale of using NSLog.
+extern void NSLog(STRING_TYPE format, ...);
+
+char *createFileSystemRepresentationOfString(STRING_TYPE str);
+STRING_TYPE createStringWithDate(DATE_TYPE date);
+
+STRING_TYPE createStringWithContentsOfFile(STRING_TYPE filename, CFStringEncoding encoding);
+
+//you can leave out any of these three components. to leave out the character, pass 0xffff.
+STRING_TYPE createStringWithStringAndCharacterAndString(STRING_TYPE str0, UniChar ch, STRING_TYPE str1);
+
+char *copyCString(STRING_TYPE str, CFStringEncoding encoding);
+
+STRING_TYPE copyCurrentProcessName(void);
+URL_TYPE    copyCurrentProcessURL(void);
+STRING_TYPE copyCurrentProcessPath(void);
+
+URL_TYPE    copyTemporaryFolderURL(void);
+STRING_TYPE copyTemporaryFolderPath(void);
+
+STRING_TYPE createStringWithAddressData(DATA_TYPE aAddressData);
+STRING_TYPE createHostNameForAddressData(DATA_TYPE aAddressData);
+
+DATA_TYPE readFile(const char *filename);
+URL_TYPE  copyURLForApplication(STRING_TYPE appName);
+
+/*	@function	copyIconDataForPath
+ *	@param	path	The POSIX path to the file or folder whose icon you want.
+ *	@result	The icon data, in IconFamily format (same as used in the 'icns' resource and in .icns files). You are responsible for releasing this object.
+ */
+DATA_TYPE copyIconDataForPath(STRING_TYPE path);
+/*	@function	copyIconDataForURL
+ *	@param	URL	The URL to the file or folder whose icon you want.
+ *	@result	The icon data, in IconFamily format (same as used in the 'icns' resource and in .icns files). You are responsible for releasing this object.
+ */
+DATA_TYPE copyIconDataForURL(URL_TYPE URL);
+
+/*	@function	createURLByMakingDirectoryAtURLWithName
+ *	@abstract	Create a directory.
+ *	@discussion	This function has a useful side effect: if you pass
+ *	 <code>NULL</code> for both parameters, this function will act basically as
+ *	 CFURL version of <code>getcwd</code>(3).
+ *
+ *	 Also, for CF clients: the allocator used to create the returned URL will
+ *	 be the allocator for the parent URL, the allocator for the name string, or
+ *	 the default allocator, in that order of preference.
+ *	@param	parent	The directory in which to create the new directory. If this is <code>NULL</code>, the current working directory (as returned by <code>getcwd</code>(3)) will be used.
+ *	@param	name	The name of the directory you want to create. If this is <code>NULL</code>, the directory specified by the URL will be created.
+ *	@result	The URL for the directory if it was successfully created (in which case, you are responsible for releasing this object); else, <code>NULL</code>.
+ */
+URL_TYPE createURLByMakingDirectoryAtURLWithName(URL_TYPE parent, STRING_TYPE name);
+
+/*	@function	createURLByCopyingFileFromURLToDirectoryURL
+ *	@param	file	The file to copy.
+ *	@param	dest	The folder to copy it to.
+ *	@result	The copy. You are responsible for releasing this object.
+ */
+URL_TYPE createURLByCopyingFileFromURLToDirectoryURL(URL_TYPE file, URL_TYPE dest);
+
+/*	@function	createPropertyListFromURL
+ *	@abstract	Reads a property list from the contents of an URL.
+ *	@discussion	Creates a property list from the data at an URL (for example, a
+ *	 file URL), and returns it.
+ *	@param	file	The file to read.
+ *	@param	mutability	A mutability-option constant indicating whether the property list (and possibly its contents) should be mutable.
+ *	@param	outFormat	If the property list is read successfully, this will point to the format of the property list. You may pass NULL if you are not interested in this information. If the property list is not read successfully, the value at this pointer will be left unchanged.
+ *	@param	outErrorString	If an error occurs, this will point to a string (which you are responsible for releasing) describing the error. You may pass NULL if you are not interested in this information. If no error occurs, the value at this pointer will be left unchanged.
+ *	@result	The property list. You are responsible for releasing this object.
+ */
+PLIST_TYPE createPropertyListFromURL(URL_TYPE file, u_int32_t mutability, CFPropertyListFormat *outFormat, STRING_TYPE *outErrorString);
+
+#endif
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/CFGrowlDefines.h
@@ -0,0 +1,38 @@
+//
+//  CFURLDefines.h
+//  Growl
+//
+//  Created by Ingmar Stein on Fri Sep 16 2005.
+//  Copyright 2005-2006 The Growl Project. All rights reserved.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+#ifndef HAVE_CFGROWLDEFINES_H
+#define HAVE_CFGROWLDEFINES_H
+
+#ifdef __OBJC__
+#	define DATA_TYPE				NSData *
+#	define DATE_TYPE				NSDate *
+#	define DICTIONARY_TYPE			NSDictionary *
+#	define MUTABLE_DICTIONARY_TYPE	NSMutableDictionary *
+#	define STRING_TYPE				NSString *
+#	define ARRAY_TYPE				NSArray *
+#	define URL_TYPE					NSURL *
+#	define PLIST_TYPE				NSObject *
+#	define OBJECT_TYPE				id
+#	define BOOL_TYPE				BOOL
+#else
+#	include <CoreFoundation/CoreFoundation.h>
+#	define DATA_TYPE				CFDataRef
+#	define DATE_TYPE				CFDateRef
+#	define DICTIONARY_TYPE			CFDictionaryRef
+#	define MUTABLE_DICTIONARY_TYPE	CFMutableDictionaryRef
+#	define STRING_TYPE				CFStringRef
+#	define ARRAY_TYPE				CFArrayRef
+#	define URL_TYPE					CFURLRef
+#	define PLIST_TYPE				CFPropertyListRef
+#	define OBJECT_TYPE				CFTypeRef
+#	define BOOL_TYPE				Boolean
+#endif
+
+#endif
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/CFMutableDictionaryAdditions.c
@@ -0,0 +1,24 @@
+//
+//  CFMutableDictionaryAdditions.c
+//  Growl
+//
+//  Created by Ingmar Stein on 29.05.05.
+//  Copyright 2005-2006 The Growl Project. All rights reserved.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+#include "CFMutableDictionaryAdditions.h"
+
+void setObjectForKey(CFMutableDictionaryRef dict, const void *key, CFTypeRef value) {
+	CFDictionarySetValue(dict, key, value);
+}
+
+void setIntegerForKey(CFMutableDictionaryRef dict, const void *key, int value) {
+	CFNumberRef num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value);
+	CFDictionarySetValue(dict, key, num);
+	CFRelease(num);
+}
+
+void setBooleanForKey(CFMutableDictionaryRef dict, const void *key, Boolean value) {
+	CFDictionarySetValue(dict, key, value ? kCFBooleanTrue : kCFBooleanFalse);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/CFMutableDictionaryAdditions.h
@@ -0,0 +1,19 @@
+//
+//  CFMutableDictionaryAdditions.h
+//  Growl
+//
+//  Created by Ingmar Stein on 29.05.05.
+//  Copyright 2005-2006 The Growl Project. All rights reserved.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+#ifndef HAVE_CFMUTABLEDICTIONARYADDITIONS_H
+#define HAVE_CFMUTABLEDICTIONARYADDITIONS_H
+
+#include "CFGrowlDefines.h"
+
+void setObjectForKey(MUTABLE_DICTIONARY_TYPE dict, const void *key, OBJECT_TYPE value);
+void setIntegerForKey(MUTABLE_DICTIONARY_TYPE dict, const void *key, int value);
+void setBooleanForKey(MUTABLE_DICTIONARY_TYPE dict, const void *key, BOOL_TYPE value);
+
+#endif
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/CFURLAdditions.c
@@ -0,0 +1,154 @@
+//
+//  CFURLAdditions.c
+//  Growl
+//
+//  Created by Karl Adam on Fri May 28 2004.
+//  Copyright 2004-2006 The Growl Project. All rights reserved.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+#include "CFURLAdditions.h"
+#include "CFGrowlAdditions.h"
+#include "CFMutableDictionaryAdditions.h"
+#include <Carbon/Carbon.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define _CFURLAliasDataKey  CFSTR("_CFURLAliasData")
+#define _CFURLStringKey     CFSTR("_CFURLString")
+#define _CFURLStringTypeKey CFSTR("_CFURLStringType")
+
+//'alias' as in the Alias Manager.
+URL_TYPE createFileURLWithAliasData(DATA_TYPE aliasData) {
+	if (!aliasData) {
+		NSLog(CFSTR("WARNING: createFileURLWithAliasData called with NULL aliasData"));
+		return NULL;
+	}
+
+	CFURLRef url = NULL;
+
+	AliasHandle alias = NULL;
+	OSStatus err = PtrToHand(CFDataGetBytePtr(aliasData), (Handle *)&alias, CFDataGetLength(aliasData));
+	if (err != noErr) {
+		NSLog(CFSTR("in createFileURLWithAliasData: Could not allocate an alias handle from %u bytes of alias data (data follows) because PtrToHand returned %li\n%@"), CFDataGetLength(aliasData), aliasData, (long)err);
+	} else {
+		CFStringRef path = NULL;
+		/*
+		 * FSResolveAlias mounts disk images or network shares to resolve
+		 * aliases, thus we resort to FSCopyAliasInfo.
+		 */
+		err = FSCopyAliasInfo(alias,
+							  /* targetName */ NULL,
+							  /* volumeName */ NULL,
+							  &path,
+							  /* whichInfo */ NULL,
+							  /* info */ NULL);
+		DisposeHandle((Handle)alias);
+		if (err != noErr) {
+			if (err != fnfErr) //ignore file-not-found; it's harmless
+				NSLog(CFSTR("in createFileURLWithAliasData: Could not resolve alias (alias data follows) because FSResolveAlias returned %li - will try path\n%@"), (long)err, aliasData);
+		} else if (path) {
+			url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, true);
+		} else {
+			NSLog(CFSTR("in createFileURLWithAliasData: FSCopyAliasInfo returned a NULL path"));
+		}
+		CFRelease(path);
+	}
+
+	return url;
+}
+
+DATA_TYPE createAliasDataWithURL(URL_TYPE theURL) {
+	//return NULL for non-file: URLs.
+	CFStringRef scheme = CFURLCopyScheme(theURL);
+	CFComparisonResult isFileURL = CFStringCompare(scheme, CFSTR("file"), kCFCompareCaseInsensitive);
+	CFRelease(scheme);
+	if (isFileURL != kCFCompareEqualTo)
+		return NULL;
+
+	CFDataRef aliasData = NULL;
+
+	FSRef fsref;
+	if (CFURLGetFSRef(theURL, &fsref)) {
+		AliasHandle alias = NULL;
+		OSStatus    err   = FSNewAlias(/*fromFile*/ NULL, &fsref, &alias);
+		if (err != noErr) {
+			NSLog(CFSTR("in createAliasDataForURL: FSNewAlias for %@ returned %li"), theURL, (long)err);
+		} else {
+			HLock((Handle)alias);
+
+			aliasData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)*alias, GetHandleSize((Handle)alias));
+
+			HUnlock((Handle)alias);
+			DisposeHandle((Handle)alias);
+		}
+	}
+
+	return aliasData;
+}
+
+//these are the type of external representations used by Dock.app.
+URL_TYPE createFileURLWithDockDescription(DICTIONARY_TYPE dict) {
+	CFURLRef url = NULL;
+
+	CFStringRef path      = CFDictionaryGetValue(dict, _CFURLStringKey);
+	CFDataRef   aliasData = CFDictionaryGetValue(dict, _CFURLAliasDataKey);
+
+	if (aliasData)
+		url = createFileURLWithAliasData(aliasData);
+
+	if (!url) {
+		if (path) {
+			CFNumberRef pathStyleNum = CFDictionaryGetValue(dict, _CFURLStringTypeKey);
+			CFURLPathStyle pathStyle = kCFURLPOSIXPathStyle;
+			
+			if (pathStyleNum)
+				CFNumberGetValue(pathStyleNum, kCFNumberIntType, &pathStyle);
+
+			char *filename = createFileSystemRepresentationOfString(path);
+			int fd = open(filename, O_RDONLY, 0);
+			free(filename);
+			if (fd != -1) {
+				struct stat sb;
+				fstat(fd, &sb);
+				close(fd);
+				url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, pathStyle, /*isDirectory*/ (sb.st_mode & S_IFDIR));
+			}
+		}
+	}
+
+	return url;
+}
+
+DICTIONARY_TYPE createDockDescriptionWithURL(URL_TYPE theURL) {
+	CFMutableDictionaryRef dict;
+
+	if (!theURL) {
+		NSLog(CFSTR("%@"), CFSTR("in createDockDescriptionWithURL: Cannot copy Dock description for a NULL URL"));
+		return NULL;
+	}
+
+	CFStringRef path     = CFURLCopyFileSystemPath(theURL, kCFURLPOSIXPathStyle);
+	CFDataRef aliasData  = createAliasDataWithURL(theURL);
+
+	if (path || aliasData) {
+		dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+		if (path) {
+			CFDictionarySetValue(dict, _CFURLStringKey, path);
+			CFRelease(path);
+			setIntegerForKey(dict, _CFURLStringTypeKey, kCFURLPOSIXPathStyle);
+		}
+
+		if (aliasData) {
+			CFDictionarySetValue(dict, _CFURLAliasDataKey, aliasData);
+			CFRelease(aliasData);
+		}
+	} else {
+		dict = NULL;
+	}
+
+	return dict;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/CFURLAdditions.h
@@ -0,0 +1,25 @@
+//
+//  CFURLAdditions.h
+//  Growl
+//
+//  Created by Karl Adam on Fri May 28 2004.
+//  Copyright 2004-2006 The Growl Project. All rights reserved.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+#ifndef HAVE_CFURLADDITIONS_H
+#define HAVE_CFURLADDITIONS_H
+
+#include <CoreFoundation/CoreFoundation.h>
+#include "CFGrowlDefines.h"
+
+//'alias' as in the Alias Manager.
+URL_TYPE createFileURLWithAliasData(DATA_TYPE aliasData);
+DATA_TYPE createAliasDataWithURL(URL_TYPE theURL);
+
+//these are the type of external representations used by Dock.app.
+URL_TYPE createFileURLWithDockDescription(DICTIONARY_TYPE dict);
+//createDockDescriptionWithURL returns NULL for non-file: URLs.
+DICTIONARY_TYPE createDockDescriptionWithURL(URL_TYPE theURL);
+
+#endif
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/GrowlAbstractSingletonObject.h
@@ -0,0 +1,71 @@
+//
+//  GrowlAbstractSingletonObject.h
+//  GBUtilities
+//
+//	Renamed from GBAbstractSingletonObject to GrowlAbstractSingletonObject.
+//  Created by Ofri Wolfus on 15/08/05.
+//  Copyright 2005-2006 The Growl Project. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+/*!
+ * @class GrowlAbstractSingletonObject
+ * @brief An Abstract Singleton Object
+ *
+ * This is an abstract object for object that should have only one instnace
+ * that is never released (singleton object).
+ * This class is thread safe.
+ */
+@interface GrowlAbstractSingletonObject : NSObject {
+	BOOL	_isInitialized;
+}
+
+/*!
+ * @brief Returns the shared instance of this class.
+ */
++ (id) sharedInstance;
+
+/*!
+ * @brief Releases and deallocates all the singletons that are subclasses of this object.
+ *
+ * Once +destroyAllSingletons has been called, no more singletons can be created
+ * and every call to [SomeSingletonSubclass sharedInstance] will return nil.
+ * Also note that a call to this method will destroy GBAbstractSingletonObject and all it's subclasses.
+ * Even though that you generally can't release a singleton object, it's dealloc message WILL be called
+ * when it's beeing destroyed.
+ *
+ * USE THIS METHOD WITH GREAT CAUTION!!!
+ */
++ (void) destroyAllSingletons;
+
+@end
+
+/*!
+ * @category GrowlSingletonObjectInit
+ * @brief A private category for subclasses only.
+ *
+ * Only subclasses should override/call methods in the category.
+ */
+@interface GrowlAbstractSingletonObject (GrowlAbstractSingletonObjectInit)
+
+/*!
+ * @brief An init method for your singleton object.
+ *
+ * Implement this in your subclass to init your shared object.
+ * You should call [super initSingleton] and return your initialized object.
+ * Never call this method directly! It'll be automatically called when needed.
+ */
+- (id) initSingleton;
+
+/*!
+ * @brief Finish and clean up whatever your singleton does.
+ *
+ * This will be called before the singleton will be destroyed.
+ * You should put whatever you would put in the -dealloc method here instead
+ * and then call [super destroy].
+ */
+- (void) destroy;
+
+@end
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/GrowlApplicationBridge.h
@@ -0,0 +1,575 @@
+//
+//  GrowlApplicationBridge.h
+//  Growl
+//
+//  Created by Evan Schoenberg on Wed Jun 16 2004.
+//  Copyright 2004-2006 The Growl Project. All rights reserved.
+//
+
+/*!
+ *	@header		GrowlApplicationBridge.h
+ *	@abstract   Defines the GrowlApplicationBridge class.
+ *	@discussion This header defines the GrowlApplicationBridge class as well as
+ *	 the GROWL_PREFPANE_BUNDLE_IDENTIFIER constant.
+ */
+
+#ifndef __GrowlApplicationBridge_h__
+#define __GrowlApplicationBridge_h__
+
+#import <Foundation/Foundation.h>
+#import <AppKit/AppKit.h>
+#import "GrowlDefines.h"
+
+//Forward declarations
+@protocol GrowlApplicationBridgeDelegate;
+
+//Internal notification when the user chooses not to install (to avoid continuing to cache notifications awaiting installation)
+#define GROWL_USER_CHOSE_NOT_TO_INSTALL_NOTIFICATION @"User chose not to install"
+
+//------------------------------------------------------------------------------
+#pragma mark -
+
+/*!
+ *	@class      GrowlApplicationBridge
+ *	@abstract   A class used to interface with Growl.
+ *	@discussion This class provides a means to interface with Growl.
+ *
+ *	 Currently it provides a way to detect if Growl is installed and launch the
+ *	 GrowlHelperApp if it's not already running.
+ */
+@interface GrowlApplicationBridge : NSObject {
+
+}
+
+/*!
+ *	@method isGrowlInstalled
+ *	@abstract Detects whether Growl is installed.
+ *	@discussion Determines if the Growl prefpane and its helper app are installed.
+ *	@result Returns YES if Growl is installed, NO otherwise.
+ */
++ (BOOL) isGrowlInstalled;
+
+/*!
+ *	@method isGrowlRunning
+ *	@abstract Detects whether GrowlHelperApp is currently running.
+ *	@discussion Cycles through the process list to find whether GrowlHelperApp is running and returns its findings.
+ *	@result Returns YES if GrowlHelperApp is running, NO otherwise.
+ */
++ (BOOL) isGrowlRunning;
+
+#pragma mark -
+
+/*!
+ *	@method setGrowlDelegate:
+ *	@abstract Set the object which will be responsible for providing and receiving Growl information.
+ *	@discussion This must be called before using GrowlApplicationBridge.
+ *
+ *	 The methods in the GrowlApplicationBridgeDelegate protocol are required
+ *	 and return the basic information needed to register with Growl.
+ *
+ *	 The methods in the GrowlApplicationBridgeDelegate_InformalProtocol
+ *	 informal protocol are individually optional.  They provide a greater
+ *	 degree of interaction between the application and growl such as informing
+ *	 the application when one of its Growl notifications is clicked by the user.
+ *
+ *	 The methods in the GrowlApplicationBridgeDelegate_Installation_InformalProtocol
+ *	 informal protocol are individually optional and are only applicable when
+ *	 using the Growl-WithInstaller.framework which allows for automated Growl
+ *	 installation.
+ *
+ *	 When this method is called, data will be collected from inDelegate, Growl
+ *	 will be launched if it is not already running, and the application will be
+ *	 registered with Growl.
+ *
+ *	 If using the Growl-WithInstaller framework, if Growl is already installed
+ *	 but this copy of the framework has an updated version of Growl, the user
+ *	 will be prompted to update automatically.
+ *
+ *	@param inDelegate The delegate for the GrowlApplicationBridge. It must conform to the GrowlApplicationBridgeDelegate protocol.
+ */
++ (void) setGrowlDelegate:(NSObject<GrowlApplicationBridgeDelegate> *)inDelegate;
+
+/*!
+ *	@method growlDelegate
+ *	@abstract Return the object responsible for providing and receiving Growl information.
+ *	@discussion See setGrowlDelegate: for details.
+ *	@result The Growl delegate.
+ */
++ (NSObject<GrowlApplicationBridgeDelegate> *) growlDelegate;
+
+#pragma mark -
+
+/*!
+ *	@method notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:
+ *	@abstract Send a Growl notification.
+ *	@discussion This is the preferred means for sending a Growl notification.
+ *	 The notification name and at least one of the title and description are
+ *	 required (all three are preferred).  All other parameters may be
+ *	 <code>nil</code> (or 0 or NO as appropriate) to accept default values.
+ *
+ *	 If using the Growl-WithInstaller framework, if Growl is not installed the
+ *	 user will be prompted to install Growl. If the user cancels, this method
+ *	 will have no effect until the next application session, at which time when
+ *	 it is called the user will be prompted again. The user is also given the
+ *	 option to not be prompted again.  If the user does choose to install Growl,
+ *	 the requested notification will be displayed once Growl is installed and
+ *	 running.
+ *
+ *	@param title		The title of the notification displayed to the user.
+ *	@param description	The full description of the notification displayed to the user.
+ *	@param notifName	The internal name of the notification. Should be human-readable, as it will be displayed in the Growl preference pane.
+ *	@param iconData		<code>NSData</code> object to show with the notification as its icon. If <code>nil</code>, the application's icon will be used instead.
+ *	@param priority		The priority of the notification. The default value is 0; positive values are higher priority and negative values are lower priority. Not all Growl displays support priority.
+ *	@param isSticky		If YES, the notification will remain on screen until clicked. Not all Growl displays support sticky notifications.
+ *	@param clickContext	A context passed back to the Growl delegate if it implements -(void)growlNotificationWasClicked: and the notification is clicked. Not all display plugins support clicking. The clickContext must be plist-encodable (completely of <code>NSString</code>, <code>NSArray</code>, <code>NSNumber</code>, <code>NSDictionary</code>, and <code>NSData</code> types).
+ */
++ (void) notifyWithTitle:(NSString *)title
+			 description:(NSString *)description
+		notificationName:(NSString *)notifName
+				iconData:(NSData *)iconData
+				priority:(signed int)priority
+				isSticky:(BOOL)isSticky
+			clickContext:(id)clickContext;
+
+/*!
+ *	@method notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:identifier:
+ *	@abstract Send a Growl notification.
+ *	@discussion This is the preferred means for sending a Growl notification.
+ *	 The notification name and at least one of the title and description are
+ *	 required (all three are preferred).  All other parameters may be
+ *	 <code>nil</code> (or 0 or NO as appropriate) to accept default values.
+ *
+ *	 If using the Growl-WithInstaller framework, if Growl is not installed the
+ *	 user will be prompted to install Growl. If the user cancels, this method
+ *	 will have no effect until the next application session, at which time when
+ *	 it is called the user will be prompted again. The user is also given the
+ *	 option to not be prompted again.  If the user does choose to install Growl,
+ *	 the requested notification will be displayed once Growl is installed and
+ *	 running.
+ *
+ *	@param title		The title of the notification displayed to the user.
+ *	@param description	The full description of the notification displayed to the user.
+ *	@param notifName	The internal name of the notification. Should be human-readable, as it will be displayed in the Growl preference pane.
+ *	@param iconData		<code>NSData</code> object to show with the notification as its icon. If <code>nil</code>, the application's icon will be used instead.
+ *	@param priority		The priority of the notification. The default value is 0; positive values are higher priority and negative values are lower priority. Not all Growl displays support priority.
+ *	@param isSticky		If YES, the notification will remain on screen until clicked. Not all Growl displays support sticky notifications.
+ *	@param clickContext	A context passed back to the Growl delegate if it implements -(void)growlNotificationWasClicked: and the notification is clicked. Not all display plugins support clicking. The clickContext must be plist-encodable (completely of <code>NSString</code>, <code>NSArray</code>, <code>NSNumber</code>, <code>NSDictionary</code>, and <code>NSData</code> types).
+ *	@param identifier	An identifier for this notification. Notifications with equal identifiers are coalesced.
+ */
++ (void) notifyWithTitle:(NSString *)title
+			 description:(NSString *)description
+		notificationName:(NSString *)notifName
+				iconData:(NSData *)iconData
+				priority:(signed int)priority
+				isSticky:(BOOL)isSticky
+			clickContext:(id)clickContext
+			  identifier:(NSString *)identifier;
+
+/*!	@method	notifyWithDictionary:
+ *	@abstract	Notifies using a userInfo dictionary suitable for passing to
+ *	 <code>NSDistributedNotificationCenter</code>.
+ *	@param	userInfo	The dictionary to notify with.
+ *	@discussion	Before Growl 0.6, your application would have posted
+ *	 notifications using <code>NSDistributedNotificationCenter</code> by
+ *	 creating a userInfo dictionary with the notification data. This had the
+ *	 advantage of allowing you to add other data to the dictionary for programs
+ *	 besides Growl that might be listening.
+ *
+ *	 This method allows you to use such dictionaries without being restricted
+ *	 to using <code>NSDistributedNotificationCenter</code>. The keys for this dictionary
+ *	 can be found in GrowlDefines.h.
+ */
++ (void) notifyWithDictionary:(NSDictionary *)userInfo;
+
+#pragma mark -
+
+/*!	@method	registerWithDictionary:
+ *	@abstract	Register your application with Growl without setting a delegate.
+ *	@discussion	When you call this method with a dictionary,
+ *	 GrowlApplicationBridge registers your application using that dictionary.
+ *	 If you pass <code>nil</code>, GrowlApplicationBridge will ask the delegate
+ *	 (if there is one) for a dictionary, and if that doesn't work, it will look
+ *	 in your application's bundle for an auto-discoverable plist.
+ *	 (XXX refer to more information on that)
+ *
+ *	 If you pass a dictionary to this method, it must include the
+ *	 <code>GROWL_APP_NAME</code> key, unless a delegate is set.
+ *
+ *	 This method is mainly an alternative to the delegate system introduced
+ *	 with Growl 0.6. Without a delegate, you cannot receive callbacks such as
+ *	 <code>-growlIsReady</code> (since they are sent to the delegate). You can,
+ *	 however, set a delegate after registering without one.
+ *
+ *	 This method was introduced in Growl.framework 0.7.
+ */
++ (BOOL) registerWithDictionary:(NSDictionary *)regDict;
+
+/*!	@method	reregisterGrowlNotifications
+ *	@abstract	Reregister the notifications for this application.
+ *	@discussion	This method does not normally need to be called.  If your
+ *	 application changes what notifications it is registering with Growl, call
+ *	 this method to have the Growl delegate's
+ *	 <code>-registrationDictionaryForGrowl</code> method called again and the
+ *	 Growl registration information updated.
+ *
+ *	 This method is now implemented using <code>-registerWithDictionary:</code>.
+ */
++ (void) reregisterGrowlNotifications;
+
+#pragma mark -
+
+/*!	@method	setWillRegisterWhenGrowlIsReady:
+ *	@abstract	Tells GrowlApplicationBridge to register with Growl when Growl
+ *	 launches (or not).
+ *	@discussion	When Growl has started listening for notifications, it posts a
+ *	 <code>GROWL_IS_READY</code> notification on the Distributed Notification
+ *	 Center. GrowlApplicationBridge listens for this notification, using it to
+ *	 perform various tasks (such as calling your delegate's
+ *	 <code>-growlIsReady</code> method, if it has one). If this method is
+ *	 called with <code>YES</code>, one of those tasks will be to reregister
+ *	 with Growl (in the manner of <code>-reregisterGrowlNotifications</code>).
+ *
+ *	 This attribute is automatically set back to <code>NO</code> (the default)
+ *	 after every <code>GROWL_IS_READY</code> notification.
+ *	@param	flag	<code>YES</code> if you want GrowlApplicationBridge to register with
+ *	 Growl when next it is ready; <code>NO</code> if not.
+ */
++ (void) setWillRegisterWhenGrowlIsReady:(BOOL)flag;
+/*!	@method	willRegisterWhenGrowlIsReady
+ *	@abstract	Reports whether GrowlApplicationBridge will register with Growl
+ *	 when Growl next launches.
+ *	@result	<code>YES</code> if GrowlApplicationBridge will register with Growl
+ *	 when next it posts GROWL_IS_READY; <code>NO</code> if not.
+ */
++ (BOOL) willRegisterWhenGrowlIsReady;
+
+#pragma mark -
+
+/*!	@method	registrationDictionaryFromDelegate
+ *	@abstract	Asks the delegate for a registration dictionary.
+ *	@discussion	If no delegate is set, or if the delegate's
+ *	 <code>-registrationDictionaryForGrowl</code> method returns
+ *	 <code>nil</code>, this method returns <code>nil</code>.
+ *
+ *	 This method does not attempt to clean up the dictionary in any way - for
+ *	 example, if it is missing the <code>GROWL_APP_NAME</code> key, the result
+ *	 will be missing it too. Use <code>+[GrowlApplicationBridge
+ *	 registrationDictionaryByFillingInDictionary:]</code> or
+ *	 <code>+[GrowlApplicationBridge
+ *	 registrationDictionaryByFillingInDictionary:restrictToKeys:]</code> to try
+ *	 to fill in missing keys.
+ *
+ *	 This method was introduced in Growl.framework 0.7.
+ *	@result A registration dictionary.
+ */
++ (NSDictionary *) registrationDictionaryFromDelegate;
+
+/*!	@method	registrationDictionaryFromBundle:
+ *	@abstract	Looks in a bundle for a registration dictionary.
+ *	@discussion	This method looks in a bundle for an auto-discoverable
+ *	 registration dictionary file using <code>-[NSBundle
+ *	 pathForResource:ofType:]</code>. If it finds one, it loads the file using
+ *	 <code>+[NSDictionary dictionaryWithContentsOfFile:]</code> and returns the
+ *	 result.
+ *
+ *	 If you pass <code>nil</code> as the bundle, the main bundle is examined.
+ *
+ *	 This method does not attempt to clean up the dictionary in any way - for
+ *	 example, if it is missing the <code>GROWL_APP_NAME</code> key, the result
+ *	 will be missing it too. Use <code>+[GrowlApplicationBridge
+ *	 registrationDictionaryByFillingInDictionary:]</code> or
+ *	 <code>+[GrowlApplicationBridge
+ *	 registrationDictionaryByFillingInDictionary:restrictToKeys:]</code> to try
+ *	 to fill in missing keys.
+ *
+ *	 This method was introduced in Growl.framework 0.7.
+ *	@result A registration dictionary.
+ */
++ (NSDictionary *) registrationDictionaryFromBundle:(NSBundle *)bundle;
+
+/*!	@method	bestRegistrationDictionary
+ *	@abstract	Obtains a registration dictionary, filled out to the best of
+ *	 GrowlApplicationBridge's knowledge.
+ *	@discussion	This method creates a registration dictionary as best
+ *	 GrowlApplicationBridge knows how.
+ *
+ *	 First, GrowlApplicationBridge contacts the Growl delegate (if there is
+ *	 one) and gets the registration dictionary from that. If no such dictionary
+ *	 was obtained, GrowlApplicationBridge looks in your application's main
+ *	 bundle for an auto-discoverable registration dictionary file. If that
+ *	 doesn't exist either, this method returns <code>nil</code>.
+ *
+ *	 Second, GrowlApplicationBridge calls
+ *	 <code>+registrationDictionaryByFillingInDictionary:</code> with whatever
+ *	 dictionary was obtained. The result of that method is the result of this
+ *	 method.
+ *
+ *	 GrowlApplicationBridge uses this method when you call
+ *	 <code>+setGrowlDelegate:</code>, or when you call
+ *	 <code>+registerWithDictionary:</code> with <code>nil</code>.
+ *
+ *	 This method was introduced in Growl.framework 0.7.
+ *	@result	A registration dictionary.
+ */
++ (NSDictionary *) bestRegistrationDictionary;
+
+#pragma mark -
+
+/*!	@method	registrationDictionaryByFillingInDictionary:
+ *	@abstract	Tries to fill in missing keys in a registration dictionary.
+ *	@discussion	This method examines the passed-in dictionary for missing keys,
+ *	 and tries to work out correct values for them. As of 0.7, it uses:
+ *
+ *	 Key							             Value
+ *	 ---							             -----
+ *	 <code>GROWL_APP_NAME</code>                 <code>CFBundleExecutableName</code>
+ *	 <code>GROWL_APP_ICON</code>                 The icon of the application.
+ *	 <code>GROWL_APP_LOCATION</code>             The location of the application.
+ *	 <code>GROWL_NOTIFICATIONS_DEFAULT</code>    <code>GROWL_NOTIFICATIONS_ALL</code>
+ *
+ *	 Keys are only filled in if missing; if a key is present in the dictionary,
+ *	 its value will not be changed.
+ *
+ *	 This method was introduced in Growl.framework 0.7.
+ *	@param	regDict	The dictionary to fill in.
+ *	@result	The dictionary with the keys filled in. This is an autoreleased
+ *	 copy of <code>regDict</code>.
+ */
++ (NSDictionary *) registrationDictionaryByFillingInDictionary:(NSDictionary *)regDict;
+/*!	@method	registrationDictionaryByFillingInDictionary:restrictToKeys:
+ *	@abstract	Tries to fill in missing keys in a registration dictionary.
+ *	@discussion	This method examines the passed-in dictionary for missing keys,
+ *	 and tries to work out correct values for them. As of 0.7, it uses:
+ *
+ *	 Key							             Value
+ *	 ---							             -----
+ *	 <code>GROWL_APP_NAME</code>                 <code>CFBundleExecutableName</code>
+ *	 <code>GROWL_APP_ICON</code>                 The icon of the application.
+ *	 <code>GROWL_APP_LOCATION</code>             The location of the application.
+ *	 <code>GROWL_NOTIFICATIONS_DEFAULT</code>    <code>GROWL_NOTIFICATIONS_ALL</code>
+ *
+ *	 Only those keys that are listed in <code>keys</code> will be filled in.
+ *	 Other missing keys are ignored. Also, keys are only filled in if missing;
+ *	 if a key is present in the dictionary, its value will not be changed.
+ *
+ *	 This method was introduced in Growl.framework 0.7.
+ *	@param	regDict	The dictionary to fill in.
+ *	@param	keys	The keys to fill in. If <code>nil</code>, any missing keys are filled in.
+ *	@result	The dictionary with the keys filled in. This is an autoreleased
+ *	 copy of <code>regDict</code>.
+ */
++ (NSDictionary *) registrationDictionaryByFillingInDictionary:(NSDictionary *)regDict restrictToKeys:(NSSet *)keys;
+
+/*!	@brief	Tries to fill in missing keys in a notification dictionary.
+ *	@param	notifDict	The dictionary to fill in.
+ *	@return	The dictionary with the keys filled in. This will be a separate instance from \a notifDict.
+ *	@discussion	This function examines the \a notifDict for missing keys, and 
+ *	 tries to get them from the last known registration dictionary. As of 1.1, 
+ *	 the keys that it will look for are:
+ *
+ *	 \li <code>GROWL_APP_NAME</code>
+ *	 \li <code>GROWL_APP_ICON</code>
+ *
+ *	@since Growl.framework 1.1
+ */
++ (NSDictionary *) notificationDictionaryByFillingInDictionary:(NSDictionary *)regDict;
+
++ (NSDictionary *) frameworkInfoDictionary;
+@end
+
+//------------------------------------------------------------------------------
+#pragma mark -
+
+/*!
+ *	@protocol GrowlApplicationBridgeDelegate
+ *	@abstract Required protocol for the Growl delegate.
+ *	@discussion The methods in this protocol are required and are called
+ *	 automatically as needed by GrowlApplicationBridge. See
+ *	 <code>+[GrowlApplicationBridge setGrowlDelegate:]</code>.
+ *	 See also <code>GrowlApplicationBridgeDelegate_InformalProtocol</code>.
+ */
+
+@protocol GrowlApplicationBridgeDelegate
+
+// -registrationDictionaryForGrowl has moved to the informal protocol as of 0.7.
+
+@end
+
+//------------------------------------------------------------------------------
+#pragma mark -
+
+/*!
+ *	@category NSObject(GrowlApplicationBridgeDelegate_InformalProtocol)
+ *	@abstract Methods which may be optionally implemented by the GrowlDelegate.
+ *	@discussion The methods in this informal protocol will only be called if implemented by the delegate.
+ */
+@interface NSObject (GrowlApplicationBridgeDelegate_InformalProtocol)
+
+/*!
+ *	@method registrationDictionaryForGrowl
+ *	@abstract Return the dictionary used to register this application with Growl.
+ *	@discussion The returned dictionary gives Growl the complete list of
+ *	 notifications this application will ever send, and it also specifies which
+ *	 notifications should be enabled by default.  Each is specified by an array
+ *	 of <code>NSString</code> objects.
+ *
+ *	 For most applications, these two arrays can be the same (if all sent
+ *	 notifications should be displayed by default).
+ *
+ *	 The <code>NSString</code> objects of these arrays will correspond to the
+ *	 <code>notificationName:</code> parameter passed in
+ *	 <code>+[GrowlApplicationBridge
+ *	 notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:]</code> calls.
+ *
+ *	 The dictionary should have the required key object pairs:
+ *	 key: GROWL_NOTIFICATIONS_ALL		object: <code>NSArray</code> of <code>NSString</code> objects
+ *	 key: GROWL_NOTIFICATIONS_DEFAULT	object: <code>NSArray</code> of <code>NSString</code> objects
+ *
+ *   The dictionary may have the following key object pairs:
+ *   key: GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES	object: <code>NSDictionary</code> of key: notification name		object: human-readable notification name
+ *
+ *	 You do not need to implement this method if you have an auto-discoverable
+ *	 plist file in your app bundle. (XXX refer to more information on that)
+ *
+ *	@result The <code>NSDictionary</code> to use for registration.
+ */
+- (NSDictionary *) registrationDictionaryForGrowl;
+
+/*!
+ *	@method applicationNameForGrowl
+ *	@abstract Return the name of this application which will be used for Growl bookkeeping.
+ *	@discussion This name is used both internally and in the Growl preferences.
+ *
+ *	 This should remain stable between different versions and incarnations of
+ *	 your application.
+ *	 For example, "SurfWriter" is a good app name, whereas "SurfWriter 2.0" and
+ *	 "SurfWriter Lite" are not.
+ *
+ *	 You do not need to implement this method if you are providing the
+ *	 application name elsewhere, meaning in an auto-discoverable plist file in
+ *	 your app bundle (XXX refer to more information on that) or in the result
+ *	 of -registrationDictionaryForGrowl.
+ *
+ *	@result The name of the application using Growl.
+ */
+- (NSString *) applicationNameForGrowl;
+
+/*!
+ *	@method applicationIconForGrowl
+ *	@abstract Return the <code>NSImage</code> to treat as the application icon.
+ *	@discussion The delegate may optionally return an <code>NSImage</code>
+ *	 object to use as the application icon. If this method is not implemented,
+ *	 {{{-applicationIconDataForGrowl}}} is tried. If that method is not
+ *	 implemented, the application's own icon is used. Neither method is
+ *	 generally needed.
+ *	@result The <code>NSImage</code> to treat as the application icon.
+ */
+- (NSImage *) applicationIconForGrowl;
+
+/*!
+ *	@method applicationIconDataForGrowl
+ *	@abstract Return the <code>NSData</code> to treat as the application icon.
+ *	@discussion The delegate may optionally return an <code>NSData</code>
+ *	 object to use as the application icon; if this is not implemented, the
+ *	 application's own icon is used.  This is not generally needed.
+ *	@result The <code>NSData</code> to treat as the application icon.
+ *	@deprecated In version 1.1, in favor of {{{-applicationIconForGrowl}}}.
+ */
+- (NSData *) applicationIconDataForGrowl;
+
+/*!
+ *	@method growlIsReady
+ *	@abstract Informs the delegate that Growl has launched.
+ *	@discussion Informs the delegate that Growl (specifically, the
+ *	 GrowlHelperApp) was launched successfully. The application can take actions
+ *   with the knowledge that Growl is installed and functional.
+ */
+- (void) growlIsReady;
+
+/*!
+ *	@method growlNotificationWasClicked:
+ *	@abstract Informs the delegate that a Growl notification was clicked.
+ *	@discussion Informs the delegate that a Growl notification was clicked.  It
+ *	 is only sent for notifications sent with a non-<code>nil</code>
+ *	 clickContext, so if you want to receive a message when a notification is
+ *	 clicked, clickContext must not be <code>nil</code> when calling
+ *	 <code>+[GrowlApplicationBridge notifyWithTitle: description:notificationName:iconData:priority:isSticky:clickContext:]</code>.
+ *	@param clickContext The clickContext passed when displaying the notification originally via +[GrowlApplicationBridge notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:].
+ */
+- (void) growlNotificationWasClicked:(id)clickContext;
+
+/*!
+ *	@method growlNotificationTimedOut:
+ *	@abstract Informs the delegate that a Growl notification timed out.
+ *	@discussion Informs the delegate that a Growl notification timed out. It
+ *	 is only sent for notifications sent with a non-<code>nil</code>
+ *	 clickContext, so if you want to receive a message when a notification is
+ *	 clicked, clickContext must not be <code>nil</code> when calling
+ *	 <code>+[GrowlApplicationBridge notifyWithTitle: description:notificationName:iconData:priority:isSticky:clickContext:]</code>.
+ *	@param clickContext The clickContext passed when displaying the notification originally via +[GrowlApplicationBridge notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:].
+ */
+- (void) growlNotificationTimedOut:(id)clickContext;
+
+@end
+
+#pragma mark -
+/*!
+ *	@category NSObject(GrowlApplicationBridgeDelegate_Installation_InformalProtocol)
+ *	@abstract Methods which may be optionally implemented by the Growl delegate when used with Growl-WithInstaller.framework.
+ *	@discussion The methods in this informal protocol will only be called if
+ *	 implemented by the delegate.  They allow greater control of the information
+ *	 presented to the user when installing or upgrading Growl from within your
+ *	 application when using Growl-WithInstaller.framework.
+ */
+@interface NSObject (GrowlApplicationBridgeDelegate_Installation_InformalProtocol)
+
+/*!
+ *	@method growlInstallationWindowTitle
+ *	@abstract Return the title of the installation window.
+ *	@discussion If not implemented, Growl will use a default, localized title.
+ *	@result An NSString object to use as the title.
+ */
+- (NSString *)growlInstallationWindowTitle;
+
+/*!
+ *	@method growlUpdateWindowTitle
+ *	@abstract Return the title of the upgrade window.
+ *	@discussion If not implemented, Growl will use a default, localized title.
+ *	@result An NSString object to use as the title.
+ */
+- (NSString *)growlUpdateWindowTitle;
+
+/*!
+ *	@method growlInstallationInformation
+ *	@abstract Return the information to display when installing.
+ *	@discussion This information may be as long or short as desired (the window
+ *	 will be sized to fit it).  It will be displayed to the user as an
+ *	 explanation of what Growl is and what it can do in your application.  It
+ *	 should probably note that no download is required to install.
+ *
+ *	 If this is not implemented, Growl will use a default, localized explanation.
+ *	@result An NSAttributedString object to display.
+ */
+- (NSAttributedString *)growlInstallationInformation;
+
+/*!
+ *	@method growlUpdateInformation
+ *	@abstract Return the information to display when upgrading.
+ *	@discussion This information may be as long or short as desired (the window
+ *	 will be sized to fit it).  It will be displayed to the user as an
+ *	 explanation that an updated version of Growl is included in your
+ *	 application and no download is required.
+ *
+ *	 If this is not implemented, Growl will use a default, localized explanation.
+ *	@result An NSAttributedString object to display.
+ */
+- (NSAttributedString *)growlUpdateInformation;
+
+@end
+
+//private
+@interface GrowlApplicationBridge (GrowlInstallationPrompt_private)
++ (void) _userChoseNotToInstallGrowl;
+@end
+
+#endif /* __GrowlApplicationBridge_h__ */
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/GrowlApplicationBridge.m
@@ -0,0 +1,908 @@
+//
+//  GrowlApplicationBridge.m
+//  Growl
+//
+//  Created by Evan Schoenberg on Wed Jun 16 2004.
+//  Copyright 2004-2006 The Growl Project. All rights reserved.
+//
+
+#import "GrowlApplicationBridge.h"
+#ifdef GROWL_WITH_INSTALLER
+#import "GrowlInstallationPrompt.h"
+#import "GrowlVersionUtilities.h"
+#endif
+#include "CFGrowlAdditions.h"
+#include "CFURLAdditions.h"
+#include "CFMutableDictionaryAdditions.h"
+#import "GrowlDefinesInternal.h"
+#import "GrowlPathUtilities.h"
+#import "GrowlPathway.h"
+
+#import <ApplicationServices/ApplicationServices.h>
+
+
+/*!
+ * The 10.3+ exception handling can only work if -fobjc-exceptions is enabled
+ */
+#if 0
+	#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3
+	# define TRY		@try {
+	# define ENDTRY		}
+	# define CATCH		@catch(NSException *localException) {
+	# define ENDCATCH	}
+	#else
+	# define TRY		NS_DURING
+	# define ENDTRY
+	# define CATCH		NS_HANDLER
+	# define ENDCATCH	NS_ENDHANDLER
+	#endif
+#else
+	# define TRY		NS_DURING
+	# define ENDTRY
+	# define CATCH		NS_HANDLER
+	# define ENDCATCH	NS_ENDHANDLER
+#endif
+
+@interface GrowlApplicationBridge (PRIVATE)
+/*!
+ *	@method launchGrowlIfInstalled
+ *	@abstract Launches GrowlHelperApp.
+ *	@discussion Launches the GrowlHelperApp if it's not already running.
+ *	 GROWL_IS_READY will be posted to the distributed notification center
+ *	 once it is ready.
+ *
+ *	 Uses <code>+_launchGrowlIfInstalledWithRegistrationDictionary:</code>.
+ *	@result Returns YES if GrowlHelperApp began launching or was already running, NO if Growl isn't installed
+ */
++ (BOOL) launchGrowlIfInstalled;
+
+/*!
+ *	@method _launchGrowlIfInstalledWithRegistrationDictionary:
+ *	@abstract Launches GrowlHelperApp and registers.
+ *	@discussion Launches the GrowlHelperApp if it's not already running, and passes it a registration dictionary.
+ *	 If Growl is turned on in the Growl prefpane, GROWL_IS_READY will be posted
+ *	 to the distributed notification center when Growl is listening for
+ *	 notifications.
+ *	@param regDict The dictionary with which to register.
+ *	@result Returns YES if GrowlHelperApp began launching or was already running, NO if Growl isn't installed
+ */
++ (BOOL) _launchGrowlIfInstalledWithRegistrationDictionary:(NSDictionary *)regDict;
+
+#ifdef GROWL_WITH_INSTALLER
++ (void) _checkForPackagedUpdateForGrowlPrefPaneBundle:(NSBundle *)growlPrefPaneBundle;
+#endif
+
+/*!	@method	_applicationNameForGrowlSearchingRegistrationDictionary:
+ *	@abstract Obtain the name of the current application.
+ *	@param regDict	The dictionary to search, or <code>nil</code> not to.
+ *	@result	The name of the current application.
+ *	@discussion	Does not call +bestRegistrationDictionary, and is therefore safe to call from it.
+ */
++ (NSString *) _applicationNameForGrowlSearchingRegistrationDictionary:(NSDictionary *)regDict;
+/*!	@method	_applicationNameForGrowlSearchingRegistrationDictionary:
+ *	@abstract Obtain the icon of the current application.
+ *	@param regDict	The dictionary to search, or <code>nil</code> not to.
+ *	@result	The icon of the current application, in IconFamily format (same as is used in 'icns' resources and .icns files).
+ *	@discussion	Does not call +bestRegistrationDictionary, and is therefore safe to call from it.
+ */
++ (NSData *) _applicationIconDataForGrowlSearchingRegistrationDictionary:(NSDictionary *)regDict;
+
+/*! @method growlProxy
+ *  @abstract Obtain (creating a connection if needed) a proxy to the Growl Helper Application
+ */
++ (NSProxy<GrowlNotificationProtocol> *) growlProxy;
+@end
+
+static NSDictionary *cachedRegistrationDictionary = nil;
+static NSString	*appName = nil;
+static NSData	*appIconData = nil;
+
+static id		delegate = nil;
+static BOOL		growlLaunched = NO;
+static NSProxy<GrowlNotificationProtocol> *growlProxy = nil;
+
+#ifdef GROWL_WITH_INSTALLER
+static NSMutableArray	*queuedGrowlNotifications = nil;
+
+static BOOL				userChoseNotToInstallGrowl = NO;
+static BOOL				promptedToInstallGrowl = NO;
+static BOOL				promptedToUpgradeGrowl = NO;
+#endif
+
+//used primarily by GIP, but could be useful elsewhere.
+static BOOL		registerWhenGrowlIsReady = NO;
+
+#pragma mark -
+
+@implementation GrowlApplicationBridge
+
++ (void) setGrowlDelegate:(NSObject<GrowlApplicationBridgeDelegate> *)inDelegate {
+	NSDistributedNotificationCenter *NSDNC = [NSDistributedNotificationCenter defaultCenter];
+
+	if (inDelegate != delegate) {
+		[delegate release];
+		delegate = [inDelegate retain];
+	}
+
+	[cachedRegistrationDictionary release];
+	cachedRegistrationDictionary = [[self bestRegistrationDictionary] retain];
+
+	//Cache the appName from the delegate or the process name
+	[appName autorelease];
+	appName = [[self _applicationNameForGrowlSearchingRegistrationDictionary:cachedRegistrationDictionary] retain];
+	if (!appName) {
+		NSLog(@"%@", @"GrowlApplicationBridge: Cannot register because the application name was not supplied and could not be determined");
+		return;
+	}
+
+	/* Cache the appIconData from the delegate if it responds to the
+	 * applicationIconDataForGrowl selector, or the application if not
+	 */
+	[appIconData autorelease];
+	appIconData = [[self _applicationIconDataForGrowlSearchingRegistrationDictionary:cachedRegistrationDictionary] retain];
+
+	//Add the observer for GROWL_IS_READY which will be triggered later if all goes well
+	[NSDNC addObserver:self
+			  selector:@selector(_growlIsReady:)
+				  name:GROWL_IS_READY
+				object:nil];
+
+	/* Watch for notification clicks if our delegate responds to the
+	 * growlNotificationWasClicked: selector. Notifications will come in on a
+	 * unique notification name based on our app name, pid and
+	 * GROWL_NOTIFICATION_CLICKED.
+	 */
+	int pid = [[NSProcessInfo processInfo] processIdentifier];
+	NSString *growlNotificationClickedName = [[NSString alloc] initWithFormat:@"%@-%d-%@",
+		appName, pid, GROWL_NOTIFICATION_CLICKED];
+	if ([delegate respondsToSelector:@selector(growlNotificationWasClicked:)])
+		[NSDNC addObserver:self
+				  selector:@selector(growlNotificationWasClicked:)
+					  name:growlNotificationClickedName
+					object:nil];
+	else
+		[NSDNC removeObserver:self
+						 name:growlNotificationClickedName
+					   object:nil];
+	[growlNotificationClickedName release];
+
+	NSString *growlNotificationTimedOutName = [[NSString alloc] initWithFormat:@"%@-%d-%@",
+		appName, pid, GROWL_NOTIFICATION_TIMED_OUT];
+	if ([delegate respondsToSelector:@selector(growlNotificationTimedOut:)])
+		[NSDNC addObserver:self
+				  selector:@selector(growlNotificationTimedOut:)
+					  name:growlNotificationTimedOutName
+					object:nil];
+	else
+		[NSDNC removeObserver:self
+						 name:growlNotificationTimedOutName
+					   object:nil];
+	[growlNotificationTimedOutName release];
+
+#ifdef GROWL_WITH_INSTALLER
+	//Determine if the user has previously told us not to ever request installation again
+	userChoseNotToInstallGrowl = [[NSUserDefaults standardUserDefaults] boolForKey:@"Growl Installation:Do Not Prompt Again"];
+#endif
+
+	growlLaunched = [self _launchGrowlIfInstalledWithRegistrationDictionary:cachedRegistrationDictionary];
+}
+
++ (NSObject<GrowlApplicationBridgeDelegate> *) growlDelegate {
+	return delegate;
+}
+
+#pragma mark -
+
++ (void) notifyWithTitle:(NSString *)title
+			 description:(NSString *)description
+		notificationName:(NSString *)notifName
+				iconData:(NSData *)iconData
+				priority:(int)priority
+				isSticky:(BOOL)isSticky
+			clickContext:(id)clickContext
+{
+	[GrowlApplicationBridge notifyWithTitle:title
+								description:description
+						   notificationName:notifName
+								   iconData:iconData
+								   priority:priority
+								   isSticky:isSticky
+							   clickContext:clickContext
+								 identifier:nil];
+}
+
+/* Send a notification to Growl for display.
+ * title, description, and notifName are required.
+ * All other id parameters may be nil to accept defaults.
+ * priority is 0 by default; isSticky is NO by default.
+ */
++ (void) notifyWithTitle:(NSString *)title
+			 description:(NSString *)description
+		notificationName:(NSString *)notifName
+				iconData:(NSData *)iconData
+				priority:(int)priority
+				isSticky:(BOOL)isSticky
+			clickContext:(id)clickContext
+			  identifier:(NSString *)identifier
+{
+	NSParameterAssert(notifName);	//Notification name is required.
+	NSParameterAssert(title || description);	//At least one of title or description is required.
+
+	// Build our noteDict from all passed parameters
+	NSMutableDictionary *noteDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
+		notifName,	 GROWL_NOTIFICATION_NAME,
+		nil];
+
+	if (title)			setObjectForKey(noteDict, GROWL_NOTIFICATION_TITLE, title);
+	if (description)	setObjectForKey(noteDict, GROWL_NOTIFICATION_DESCRIPTION, description);
+	if (iconData)		setObjectForKey(noteDict, GROWL_NOTIFICATION_ICON, iconData);
+	if (clickContext)	setObjectForKey(noteDict, GROWL_NOTIFICATION_CLICK_CONTEXT, clickContext);
+	if (priority)		setIntegerForKey(noteDict, GROWL_NOTIFICATION_PRIORITY, priority);
+	if (isSticky)		setBooleanForKey(noteDict, GROWL_NOTIFICATION_STICKY, isSticky);
+	if (identifier)		setObjectForKey(noteDict, GROWL_NOTIFICATION_IDENTIFIER, identifier);
+
+	[self notifyWithDictionary:noteDict];
+	[noteDict release];
+}
+
++ (void) notifyWithDictionary:(NSDictionary *)userInfo {
+	//post it.
+	if (growlLaunched) {		
+		NSProxy<GrowlNotificationProtocol> *currentGrowlProxy = [self growlProxy];
+
+		//Make sure we have everything that we need (that we can retrieve from the registration dictionary).
+		userInfo = [self notificationDictionaryByFillingInDictionary:userInfo];
+
+		if (currentGrowlProxy) {
+			//Post to Growl via GrowlApplicationBridgePathway
+			TRY
+				[currentGrowlProxy postNotificationWithDictionary:userInfo];
+			ENDTRY
+			CATCH
+				NSLog(@"GrowlApplicationBridge: exception while sending notification: %@", localException);
+			ENDCATCH
+		} else {
+			//NSLog(@"GrowlApplicationBridge: could not find local GrowlApplicationBridgePathway, falling back to NSDistributedNotificationCenter");
+
+			//DNC needs a plist. this means we must pass data, not an NSImage.
+			Class     NSImageClass = [NSImage class];
+			NSImage          *icon = [userInfo objectForKey:GROWL_NOTIFICATION_ICON];
+			NSImage       *appIcon = [userInfo objectForKey:GROWL_NOTIFICATION_APP_ICON];
+			BOOL       iconIsImage =    icon &&    [icon isKindOfClass:NSImageClass];
+			BOOL    appIconIsImage = appIcon && [appIcon isKindOfClass:NSImageClass];
+			if (iconIsImage || appIconIsImage) {
+				NSMutableDictionary *mUserInfo = [userInfo mutableCopy];
+				//notification icon.
+				if (iconIsImage)
+					[mUserInfo setObject:[icon TIFFRepresentation] forKey:GROWL_NOTIFICATION_ICON];
+				//per-notification application icon.
+				if (appIconIsImage)
+					[mUserInfo setObject:[appIcon TIFFRepresentation] forKey:GROWL_NOTIFICATION_APP_ICON];
+
+				userInfo = [mUserInfo autorelease];
+			}
+
+			//Post to Growl via NSDistributedNotificationCenter
+			[[NSDistributedNotificationCenter defaultCenter] postNotificationName:GROWL_NOTIFICATION
+																		   object:nil
+																		 userInfo:userInfo
+															   deliverImmediately:NO];
+		}
+	} else {
+#ifdef GROWL_WITH_INSTALLER
+		/*if Growl launches, and the user hasn't already said NO to installing
+		 *	it, store this notification for posting
+		 */
+		if (!([self isGrowlInstalled] || userChoseNotToInstallGrowl)) {
+			if (!queuedGrowlNotifications)
+				queuedGrowlNotifications = [[NSMutableArray alloc] init];
+			[queuedGrowlNotifications addObject:userInfo];
+
+			//if we have not already asked the user to install Growl, do it now
+			if (!promptedToInstallGrowl) {
+				[GrowlInstallationPrompt showInstallationPrompt];
+				promptedToInstallGrowl = YES;
+			}
+		}
+#endif
+	}
+}
+
+#pragma mark -
+
++ (BOOL) isGrowlInstalled {
+	return ([GrowlPathUtilities growlPrefPaneBundle] != nil);
+}
+
++ (BOOL) isGrowlRunning {
+	BOOL growlIsRunning = NO;
+	ProcessSerialNumber PSN = { kNoProcess, kNoProcess };
+
+	while (GetNextProcess(&PSN) == noErr) {
+		CFDictionaryRef infoDict = ProcessInformationCopyDictionary(&PSN, kProcessDictionaryIncludeAllInformationMask);
+		CFStringRef bundleId = CFDictionaryGetValue(infoDict, kCFBundleIdentifierKey);
+
+		if (bundleId && CFStringCompare(bundleId, CFSTR("com.Growl.GrowlHelperApp"), 0) == kCFCompareEqualTo) {
+			growlIsRunning = YES;
+			CFRelease(infoDict);
+			break;
+		}
+		CFRelease(infoDict);
+	}
+
+	return growlIsRunning;
+}
+
++ (void) displayInstallationPromptIfNeeded {
+#ifdef GROWL_WITH_INSTALLER
+    //if we have not already asked the user to install Growl, do it now
+    if (!promptedToInstallGrowl) {
+        [GrowlInstallationPrompt showInstallationPrompt];
+        promptedToInstallGrowl = YES;
+    }
+#endif
+}
+
+#pragma mark -
+
++ (BOOL) registerWithDictionary:(NSDictionary *)regDict {
+	if (regDict)
+		regDict = [self registrationDictionaryByFillingInDictionary:regDict];
+	else
+		regDict = [self bestRegistrationDictionary];
+
+	[cachedRegistrationDictionary release];
+	cachedRegistrationDictionary = [regDict retain];
+
+	return [self _launchGrowlIfInstalledWithRegistrationDictionary:regDict];
+}
+
++ (void) reregisterGrowlNotifications {
+	[self registerWithDictionary:nil];
+}
+
++ (void) setWillRegisterWhenGrowlIsReady:(BOOL)flag {
+	registerWhenGrowlIsReady = flag;
+}
++ (BOOL) willRegisterWhenGrowlIsReady {
+	return registerWhenGrowlIsReady;
+}
+
+#pragma mark -
+
++ (NSDictionary *) registrationDictionaryFromDelegate {
+	NSDictionary *regDict = nil;
+
+	if (delegate && [delegate respondsToSelector:@selector(registrationDictionaryForGrowl)])
+		regDict = [delegate registrationDictionaryForGrowl];
+
+	return regDict;
+}
+
++ (NSDictionary *) registrationDictionaryFromBundle:(NSBundle *)bundle {
+	if (!bundle) bundle = [NSBundle mainBundle];
+
+	NSDictionary *regDict = nil;
+
+	NSString *regDictPath = [bundle pathForResource:@"Growl Registration Ticket" ofType:GROWL_REG_DICT_EXTENSION];
+	if (regDictPath) {
+		regDict = [NSDictionary dictionaryWithContentsOfFile:regDictPath];
+		if (!regDict)
+			NSLog(@"GrowlApplicationBridge: The bundle at %@ contains a registration dictionary, but it is not a valid property list. Please tell this application's developer.", [bundle bundlePath]);
+	}
+
+	return regDict;
+}
+
++ (NSDictionary *) bestRegistrationDictionary {
+	NSDictionary *registrationDictionary = [self registrationDictionaryFromDelegate];
+	if (!registrationDictionary) {
+		registrationDictionary = [self registrationDictionaryFromBundle:nil];
+		if (!registrationDictionary)
+			NSLog(@"GrowlApplicationBridge: The Growl delegate did not supply a registration dictionary, and the app bundle at %@ does not have one. Please tell this application's developer.", [[NSBundle mainBundle] bundlePath]);
+	}
+
+	return [self registrationDictionaryByFillingInDictionary:registrationDictionary];
+}
+
+#pragma mark -
+
++ (NSDictionary *) registrationDictionaryByFillingInDictionary:(NSDictionary *)regDict {
+	return [self registrationDictionaryByFillingInDictionary:regDict restrictToKeys:nil];
+}
+
++ (NSDictionary *) registrationDictionaryByFillingInDictionary:(NSDictionary *)regDict restrictToKeys:(NSSet *)keys {
+	if (!regDict) return nil;
+
+	NSMutableDictionary *mRegDict = [regDict mutableCopy];
+
+	if ((!keys) || [keys containsObject:GROWL_APP_NAME]) {
+		if (![mRegDict objectForKey:GROWL_APP_NAME]) {
+			if (!appName)
+				appName = [[self _applicationNameForGrowlSearchingRegistrationDictionary:regDict] retain];
+
+			[mRegDict setObject:appName
+			             forKey:GROWL_APP_NAME];
+		}
+	}
+
+	if ((!keys) || [keys containsObject:GROWL_APP_ICON]) {
+		if (![mRegDict objectForKey:GROWL_APP_ICON]) {
+			if (!appIconData)
+				appIconData = [[self _applicationIconDataForGrowlSearchingRegistrationDictionary:regDict] retain];
+			if (appIconData)
+				[mRegDict setObject:appIconData forKey:GROWL_APP_ICON];
+		}
+	}
+
+	if ((!keys) || [keys containsObject:GROWL_APP_LOCATION]) {
+		if (![mRegDict objectForKey:GROWL_APP_LOCATION]) {
+			NSURL *myURL = copyCurrentProcessURL();
+			if (myURL) {
+				NSDictionary *file_data = createDockDescriptionWithURL(myURL);
+				if (file_data) {
+					NSDictionary *location = [[NSDictionary alloc] initWithObjectsAndKeys:file_data, @"file-data", nil];
+					[file_data release];
+					[mRegDict setObject:location forKey:GROWL_APP_LOCATION];
+					[location release];
+				} else {
+					[mRegDict removeObjectForKey:GROWL_APP_LOCATION];
+				}
+				[NSMakeCollectable(myURL) release];
+			}
+		}
+	}
+
+	if ((!keys) || [keys containsObject:GROWL_NOTIFICATIONS_DEFAULT]) {
+		if (![mRegDict objectForKey:GROWL_NOTIFICATIONS_DEFAULT]) {
+			NSArray *all = [mRegDict objectForKey:GROWL_NOTIFICATIONS_ALL];
+			if (all)
+				[mRegDict setObject:all forKey:GROWL_NOTIFICATIONS_DEFAULT];
+		}
+	}
+
+	if ((!keys) || [keys containsObject:GROWL_APP_ID])
+		if (![mRegDict objectForKey:GROWL_APP_ID])
+			[mRegDict setObject:(NSString *)CFBundleGetIdentifier(CFBundleGetMainBundle()) forKey:GROWL_APP_ID];
+
+	return [mRegDict autorelease];
+}
+
++ (NSDictionary *) notificationDictionaryByFillingInDictionary:(NSDictionary *)notifDict {
+	NSMutableDictionary *mNotifDict = [notifDict mutableCopy];
+
+	if (![mNotifDict objectForKey:GROWL_APP_NAME]) {
+		if (!appName)
+			appName = [[self _applicationNameForGrowlSearchingRegistrationDictionary:cachedRegistrationDictionary] retain];
+
+		if (appName) {
+			[mNotifDict setObject:appName
+			               forKey:GROWL_APP_NAME];
+		}
+	}
+
+	if (![mNotifDict objectForKey:GROWL_APP_ICON]) {
+		if (!appIconData)
+			appIconData = [[self _applicationIconDataForGrowlSearchingRegistrationDictionary:cachedRegistrationDictionary] retain];
+
+		if (appIconData) {
+			[mNotifDict setObject:appIconData
+			               forKey:GROWL_APP_ICON];
+		}
+	}
+
+	//Only include the PID when there's a click context. We do this because NSDNC imposes a 15-MiB limit on the serialized notification, and we wouldn't want to overrun it because of a 4-byte PID.
+	if ([mNotifDict objectForKey:GROWL_NOTIFICATION_CLICK_CONTEXT] && ![mNotifDict objectForKey:GROWL_APP_PID]) {
+		NSNumber *pidNum = [[NSNumber alloc] initWithInt:[[NSProcessInfo processInfo] processIdentifier]];
+
+		[mNotifDict setObject:pidNum
+		               forKey:GROWL_APP_PID];
+
+		[pidNum release];
+	}
+
+	return [mNotifDict autorelease];
+}
+
++ (NSDictionary *) frameworkInfoDictionary {
+#ifdef GROWL_WITH_INSTALLER
+	return (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetBundleWithIdentifier(CFSTR("com.growl.growlwithinstallerframework")));
+#else
+	return (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetBundleWithIdentifier(CFSTR("com.growl.growlframework")));
+#endif
+}
+
+#pragma mark -
+#pragma mark Private methods
+
++ (NSString *) _applicationNameForGrowlSearchingRegistrationDictionary:(NSDictionary *)regDict {
+	NSString *applicationNameForGrowl = nil;
+
+	if (delegate && [delegate respondsToSelector:@selector(applicationNameForGrowl)])
+		applicationNameForGrowl = [delegate applicationNameForGrowl];
+
+	if (!applicationNameForGrowl) {
+		applicationNameForGrowl = [regDict objectForKey:GROWL_APP_NAME];
+
+		if (!applicationNameForGrowl)
+			applicationNameForGrowl = [[NSProcessInfo processInfo] processName];
+	}
+
+	return applicationNameForGrowl;
+}
++ (NSData *) _applicationIconDataForGrowlSearchingRegistrationDictionary:(NSDictionary *)regDict {
+	NSData *iconData = nil;
+
+	if (delegate) {
+		if ([delegate respondsToSelector:@selector(applicationIconForGrowl)])
+			iconData = (NSData *)[delegate applicationIconForGrowl];
+		else if ([delegate respondsToSelector:@selector(applicationIconDataForGrowl)])
+			iconData = [delegate applicationIconDataForGrowl];
+	}
+
+	if (!iconData)
+		iconData = [regDict objectForKey:GROWL_APP_ICON];
+
+	if (iconData && [iconData isKindOfClass:[NSImage class]])
+		iconData = [(NSImage *)iconData TIFFRepresentation];
+
+	if (!iconData) {
+		NSURL *URL = copyCurrentProcessURL();
+		iconData = [copyIconDataForURL(URL) autorelease];
+		[NSMakeCollectable(URL) release];
+	}
+
+	return iconData;
+}
+
+/*Selector called when a growl notification is clicked.  This should never be
+ *	called manually, and the calling observer should only be registered if the
+ *	delegate responds to growlNotificationWasClicked:.
+ */
++ (void) growlNotificationWasClicked:(NSNotification *)notification {
+	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+	[delegate growlNotificationWasClicked:
+		[[notification userInfo] objectForKey:GROWL_KEY_CLICKED_CONTEXT]];
+	[pool drain];
+}
++ (void) growlNotificationTimedOut:(NSNotification *)notification {
+	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+	[delegate growlNotificationTimedOut:
+		[[notification userInfo] objectForKey:GROWL_KEY_CLICKED_CONTEXT]];
+	[pool drain];
+}
+
+#pragma mark -
+
+//When a connection dies, release our reference to its proxy
++ (void) connectionDidDie:(NSNotification *)notification {
+	[[NSNotificationCenter defaultCenter] removeObserver:self
+													name:NSConnectionDidDieNotification
+												  object:[notification object]];
+	[growlProxy release]; growlProxy = nil;
+}
+
++ (NSProxy<GrowlNotificationProtocol> *) growlProxy {
+	if (!growlProxy) {
+		NSConnection *connection = [NSConnection connectionWithRegisteredName:@"GrowlApplicationBridgePathway" host:nil];
+		if (connection) {
+			[[NSNotificationCenter defaultCenter] addObserver:self
+													 selector:@selector(connectionDidDie:)
+														 name:NSConnectionDidDieNotification
+													   object:connection];
+			
+			TRY
+			{
+				NSDistantObject *theProxy = [connection rootProxy];
+				if ([theProxy respondsToSelector:@selector(registerApplicationWithDictionary:)]) {
+					[theProxy setProtocolForProxy:@protocol(GrowlNotificationProtocol)];
+					growlProxy = [(NSProxy<GrowlNotificationProtocol> *)theProxy retain];
+				} else {
+					NSLog(@"Received a fake GrowlApplicationBridgePathway object. Some other application is interfering with Growl, or something went horribly wrong. Please file a bug report.");
+					growlProxy = nil;
+				}
+			}
+			ENDTRY
+				CATCH
+			{
+				NSLog(@"GrowlApplicationBridge: exception while sending notification: %@", localException);
+				growlProxy = nil;
+			}
+			ENDCATCH
+		}
+	}
+	
+	return growlProxy;
+}
+
++ (void) _growlIsReady:(NSNotification *)notification {
+#pragma unused(notification)
+	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+	//Growl has now launched; we may get here with (growlLaunched == NO) when the user first installs
+	growlLaunched = YES;
+
+	//Inform our delegate if it is interested
+	if ([delegate respondsToSelector:@selector(growlIsReady)])
+		[delegate growlIsReady];
+
+	//Post a notification locally
+	[[NSNotificationCenter defaultCenter] postNotificationName:GROWL_IS_READY
+														object:nil
+													  userInfo:nil];
+
+	//Stop observing for GROWL_IS_READY
+	[[NSDistributedNotificationCenter defaultCenter] removeObserver:self
+															   name:GROWL_IS_READY
+															 object:nil];
+
+	//register (fixes #102: this is necessary if we got here by Growl having just been installed)
+	if (registerWhenGrowlIsReady) {
+		[self reregisterGrowlNotifications];
+		registerWhenGrowlIsReady = NO;
+	}
+
+#ifdef GROWL_WITH_INSTALLER
+	//Perform any queued notifications
+	NSEnumerator *enumerator = [queuedGrowlNotifications objectEnumerator];
+	NSDictionary *noteDict;
+
+	//Configure the growl proxy if it isn't currently configured
+	NSProxy<GrowlNotificationProtocol> *currentGrowlProxy = [self growlProxy];
+	
+	while ((noteDict = [enumerator nextObject])) {		
+		if (currentGrowlProxy) {
+			//Post to Growl via GrowlApplicationBridgePathway
+			NS_DURING
+				[currentGrowlProxy postNotificationWithDictionary:noteDict];
+			NS_HANDLER
+				NSLog(@"GrowlApplicationBridge: exception while sending notification: %@", localException);
+			NS_ENDHANDLER
+		} else {
+			//Post to Growl via NSDistributedNotificationCenter
+			NSLog(@"GrowlApplicationBridge: could not find local GrowlApplicationBridgePathway, falling back to NSDistributedNotificationCenter");
+			[[NSDistributedNotificationCenter defaultCenter] postNotificationName:GROWL_NOTIFICATION
+																		   object:NULL
+																		 userInfo:noteDict
+															   deliverImmediately:FALSE];
+		}
+	}
+
+	[queuedGrowlNotifications release]; queuedGrowlNotifications = nil;
+#endif
+	
+	[pool drain];
+}
+
+#ifdef GROWL_WITH_INSTALLER
+/*Sent to us by GrowlInstallationPrompt if the user clicks Cancel so we can
+ *	avoid prompting again this session (or ever if they checked Don't Ask Again)
+ */
++ (void) _userChoseNotToInstallGrowl {
+	//Note the user's action so we stop queueing notifications, etc.
+	userChoseNotToInstallGrowl = YES;
+
+	//Clear our queued notifications; we won't be needing them
+	[queuedGrowlNotifications release]; queuedGrowlNotifications = nil;
+}
+
+// Check against our current version number and ensure the installed Growl pane is the same or later
++ (void) _checkForPackagedUpdateForGrowlPrefPaneBundle:(NSBundle *)growlPrefPaneBundle {
+	NSString *ourGrowlPrefPaneInfoPath;
+	NSDictionary *infoDictionary;
+	NSString *packagedVersion, *installedVersion;
+	BOOL upgradeIsAvailable;
+
+	ourGrowlPrefPaneInfoPath = [[NSBundle bundleWithIdentifier:@"com.growl.growlwithinstallerframework"] pathForResource:@"GrowlPrefPaneInfo"
+																												  ofType:@"plist"];
+
+	NSObject *infoPropertyList = createPropertyListFromURL([NSURL fileURLWithPath:ourGrowlPrefPaneInfoPath],
+														   kCFPropertyListImmutable,
+														   /* outFormat */ NULL, /* outErrorString */ NULL);
+	NSDictionary *infoDict = ([infoPropertyList isKindOfClass:[NSDictionary class]] ? (NSDictionary *)infoPropertyList : nil);
+
+	packagedVersion = [infoDict objectForKey:(NSString *)kCFBundleVersionKey];
+
+	infoDictionary = [growlPrefPaneBundle infoDictionary];
+	installedVersion = [infoDictionary objectForKey:(NSString *)kCFBundleVersionKey];
+
+	//If the installed version is earlier than our packaged version, we can offer an upgrade.
+	upgradeIsAvailable = (compareVersionStringsTranslating1_0To0_5(packagedVersion, installedVersion) == kCFCompareGreaterThan);
+	if (upgradeIsAvailable && !promptedToUpgradeGrowl) {
+		NSString	*lastDoNotPromptVersion = [[NSUserDefaults standardUserDefaults] objectForKey:@"Growl Update:Do Not Prompt Again:Last Version"];
+
+		if (!lastDoNotPromptVersion ||
+			(compareVersionStringsTranslating1_0To0_5(packagedVersion, lastDoNotPromptVersion) == kCFCompareGreaterThan))
+		{
+			[GrowlInstallationPrompt showUpdatePromptForVersion:packagedVersion];
+			promptedToUpgradeGrowl = YES;
+		}
+	}
+	[infoDict release];
+}
+#endif
+
+#pragma mark -
+
++ (OSStatus) getPSN:(struct ProcessSerialNumber *)outAppPSN forAppWithBundleAtPath:(NSString *)appPath {
+	OSStatus err;
+	while ((err = GetNextProcess(outAppPSN)) == noErr) {
+		NSDictionary *dict = [NSMakeCollectable(ProcessInformationCopyDictionary(outAppPSN, kProcessDictionaryIncludeAllInformationMask)) autorelease];
+		NSString *bundlePath = [dict objectForKey:@"BundlePath"];
+		if ([bundlePath isEqualToString:appPath]) {
+			//Match!
+			break;
+		}
+	}
+	return err;
+}
+
++ (BOOL) launchApplicationWithBundleAtPath:(NSString *)appPath openDocumentURL:(NSURL *)regItemURL {
+	const struct LSLaunchURLSpec spec = {
+		.appURL = (CFURLRef)[NSURL fileURLWithPath:appPath],
+		.itemURLs = (CFArrayRef)(regItemURL ? [NSArray arrayWithObject:regItemURL] : nil),
+		.passThruParams = NULL,
+		.launchFlags = kLSLaunchDontAddToRecents | kLSLaunchDontSwitch | kLSLaunchNoParams | kLSLaunchAsync,
+		.asyncRefCon = NULL
+	};
+	OSStatus err = LSOpenFromURLSpec(&spec, NULL);
+	if (err != noErr) {
+		NSLog(@"Could not launch application at path %@ (with document %@) because LSOpenFromURLSpec returned %i (%s)", appPath, regItemURL, err, GetMacOSStatusCommentString(err));
+	}
+	return (err == noErr);
+}
++ (BOOL) sendOpenEventToProcessWithProcessSerialNumber:(struct ProcessSerialNumber *)appPSN openDocumentURL:(NSURL *)regItemURL {
+	OSStatus err;
+	BOOL success = NO;
+	AEStreamRef stream = AEStreamCreateEvent(kCoreEventClass, kAEOpenDocuments,
+											 //Target application
+											 typeProcessSerialNumber, appPSN, sizeof(*appPSN),
+											 kAutoGenerateReturnID, kAnyTransactionID);
+	if (!stream) {
+		NSLog(@"%@: Could not create open-document event to register this application with Growl", [self class]);
+	} else {
+		if (regItemURL) {
+			NSString *regItemURLString = [regItemURL absoluteString];
+			NSData *regItemURLUTF8Data = [regItemURLString dataUsingEncoding:NSUTF8StringEncoding];
+			err = AEStreamWriteKeyDesc(stream, keyDirectObject, typeFileURL, [regItemURLUTF8Data bytes], [regItemURLUTF8Data length]);
+			if (err != noErr) {
+				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));
+			}
+		}
+
+		AppleEvent event;
+		err = AEStreamClose(stream, &event);
+		if (err != noErr) {
+			NSLog(@"%@: Could not finish open-document event to register this application with Growl because AEStreamClose returned %li/%s", [self class], (long)err, GetMacOSStatusCommentString(err));
+		} else {
+			err = AESendMessage(&event, /*reply*/ NULL, kAENoReply | kAEDontReconnect | kAENeverInteract | kAEDontRecord, kAEDefaultTimeout);
+			if (err != noErr) {
+				NSLog(@"%@: Could not send open-document event to register this application with Growl because AESend returned %li/%s", [self class], (long)err, GetMacOSStatusCommentString(err));
+			}
+
+			AEDisposeDesc(&event);
+		}
+
+		success = (err == noErr);
+	}
+
+	return success;
+}
++ (BOOL) _launchGrowlIfInstalledWithRegistrationDictionary:(NSDictionary *)regDict {
+	BOOL success = NO;
+	NSBundle *growlPrefPaneBundle = nil;
+	NSString *growlHelperAppPath;
+
+	//First look for a running GHA. It might not actually be within a Growl prefpane bundle.
+	growlHelperAppPath = [[GrowlPathUtilities runningHelperAppBundle] bundlePath];
+	if (growlHelperAppPath) {
+		//The GHA bundle path should be: .../Growl.prefPane/Contents/Resources/GrowlHelperApp.app
+		NSArray *growlHelperAppPathComponents = [growlHelperAppPath pathComponents];
+		NSString *growlPrefPaneBundlePath = [NSString pathWithComponents:[growlHelperAppPathComponents subarrayWithRange:(NSRange){ 0UL, [growlHelperAppPathComponents count] - 3UL }]];
+		growlPrefPaneBundle = [NSBundle bundleWithPath:growlPrefPaneBundlePath];
+		//Make sure we actually got a Growl.prefPane and not, say, a Growl project folder. (NSBundle can be liberal in its acceptance of a directory as a bundle at times.)
+		if (![[growlPrefPaneBundle bundleIdentifier] isEqualToString:GROWL_PREFPANE_BUNDLE_IDENTIFIER])
+			growlPrefPaneBundle = nil;
+	}
+	//If we didn't get a Growl prefpane bundle by finding the bundle that contained GHA, look it up directly.
+	if (!growlPrefPaneBundle) {
+		growlPrefPaneBundle = [GrowlPathUtilities growlPrefPaneBundle];
+	}
+	//If we don't already have the path to a running GHA, then…
+	if (!growlHelperAppPath) {
+		//Look for an installed-but-not-running GHA.
+		growlHelperAppPath = [growlPrefPaneBundle pathForResource:@"GrowlHelperApp"
+														   ofType:@"app"];
+	}
+
+#ifdef GROWL_WITH_INSTALLER
+	if (growlPrefPaneBundle) {
+		/* Check against our current version number and ensure the installed Growl pane is the same or later */
+		[self _checkForPackagedUpdateForGrowlPrefPaneBundle:growlPrefPaneBundle];
+	}
+#endif
+
+	//Houston, we are go for launch.
+	if (growlHelperAppPath) {
+		//Let's launch in the background (requires sending the Apple Event ourselves, as LS may activate the application anyway if it's already running)
+		NSURL *appURL = [NSURL fileURLWithPath:growlHelperAppPath];
+		if (appURL) {
+			OSStatus err;
+
+			//Find the PSN for GrowlHelperApp. (We'll need this later.)
+			struct ProcessSerialNumber appPSN = {
+				0, kNoProcess
+			};
+			err = [self getPSN:&appPSN forAppWithBundleAtPath:growlHelperAppPath];
+			BOOL foundGrowlProcess = (err == noErr);
+			BOOL foundNoGrowlProcess = (err == procNotFound);
+
+			//If both of these are false, the process search failed with an error (and I don't mean procNotFound).
+			if (foundGrowlProcess || foundNoGrowlProcess) {
+				NSURL *regItemURL = nil;
+				BOOL passRegDict = NO;
+
+				if (regDict) {
+					NSString *regDictFileName;
+					NSString *regDictPath;
+
+					//Obtain a truly unique file name
+					CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
+					CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuid);
+					CFRelease(uuid);
+					regDictFileName = [[NSString stringWithFormat:@"%@-%u-%@", [self _applicationNameForGrowlSearchingRegistrationDictionary:regDict], getpid(), (NSString *)uuidString] stringByAppendingPathExtension:GROWL_REG_DICT_EXTENSION];
+					CFRelease(uuidString);
+					if ([regDictFileName length] > NAME_MAX)
+						regDictFileName = [[regDictFileName substringToIndex:(NAME_MAX - [GROWL_REG_DICT_EXTENSION length])] stringByAppendingPathExtension:GROWL_REG_DICT_EXTENSION];
+
+					//make sure it's within pathname length constraints
+					regDictPath = [NSTemporaryDirectory() stringByAppendingPathComponent:regDictFileName];
+					if ([regDictPath length] > PATH_MAX)
+						regDictPath = [[regDictPath substringToIndex:(PATH_MAX - [GROWL_REG_DICT_EXTENSION length])] stringByAppendingPathExtension:GROWL_REG_DICT_EXTENSION];
+
+					//Write the registration dictionary out to the temporary directory
+					NSData *plistData;
+					NSString *error;
+					plistData = [NSPropertyListSerialization dataFromPropertyList:regDict
+																		   format:NSPropertyListBinaryFormat_v1_0
+																 errorDescription:&error];
+					if (plistData) {
+						if (![plistData writeToFile:regDictPath atomically:NO])
+							NSLog(@"GrowlApplicationBridge: Error writing registration dictionary at %@", regDictPath);
+					} else {
+						NSLog(@"GrowlApplicationBridge: Error writing registration dictionary at %@: %@", regDictPath, error);
+						NSLog(@"GrowlApplicationBridge: Registration dictionary follows\n%@", regDict);
+						[error release];
+					}
+
+					if ([[NSFileManager defaultManager] fileExistsAtPath:regDictPath]) {
+						regItemURL = [NSURL fileURLWithPath:regDictPath];
+						passRegDict = YES;
+					}
+				}
+
+				if (foundNoGrowlProcess)
+					success = [self launchApplicationWithBundleAtPath:growlHelperAppPath openDocumentURL:(passRegDict ? regItemURL : nil)];
+				else
+					success = [self sendOpenEventToProcessWithProcessSerialNumber:&appPSN openDocumentURL:(passRegDict ? regItemURL : nil)];
+			}
+		}
+	}
+
+	return success;
+}
+
+/*	+ (BOOL)launchGrowlIfInstalled
+ *
+ *Returns YES if the Growl helper app began launching or was already running.
+ *Returns NO and performs no other action if the Growl prefPane is not properly
+ *	installed.
+ *If Growl is installed but disabled, the application will be registered and
+ *	GrowlHelperApp will then quit.  This method will still return YES if Growl
+ *	is installed but disabled.
+ */
++ (BOOL) launchGrowlIfInstalled {
+	return [self _launchGrowlIfInstalledWithRegistrationDictionary:nil];
+}
+
+@end
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/GrowlDefines.h
@@ -0,0 +1,348 @@
+//
+//  GrowlDefines.h
+//
+
+#ifndef _GROWLDEFINES_H
+#define _GROWLDEFINES_H
+
+#ifdef __OBJC__
+#define XSTR(x) (@x)
+#define STRING_TYPE NSString *
+#else
+#define XSTR CFSTR
+#define STRING_TYPE CFStringRef
+#endif
+
+/*!	@header GrowlDefines.h
+ *	@abstract   Defines all the notification keys.
+ *	@discussion Defines all the keys used for registration with Growl and for
+ *	 Growl notifications.
+ *
+ *	 Most applications should use the functions or methods of Growl.framework
+ *	 instead of posting notifications such as those described here.
+ *	@updated 2004-01-25
+ */
+
+// UserInfo Keys for Registration
+#pragma mark UserInfo Keys for Registration
+
+/*!	@group Registration userInfo keys */
+/*	@abstract	Keys for the userInfo dictionary of a GROWL_APP_REGISTRATION distributed notification.
+ *	@discussion The values of these keys describe the application and the
+ *	 notifications it may post.
+ *
+ *	 Your application must register with Growl before it can post Growl
+ *	 notifications (and have them not be ignored). However, as of Growl 0.6,
+ *	 posting GROWL_APP_REGISTRATION notifications directly is no longer the
+ *	 preferred way to register your application. Your application should instead
+ *	 use Growl.framework's delegate system.
+ *	 See +[GrowlApplicationBridge setGrowlDelegate:] or Growl_SetDelegate for
+ *	 more information.
+ */
+
+/*!	@defined GROWL_APP_NAME
+ *	@abstract The name of your application.
+ *	@discussion The name of your application. This should remain stable between
+ *	 different versions and incarnations of your application.
+ *	 For example, "SurfWriter" is a good app name, whereas "SurfWriter 2.0" and
+ *	 "SurfWriter Lite" are not.
+ */
+#define GROWL_APP_NAME					XSTR("ApplicationName")
+/*!	@defined GROWL_APP_ID
+ *	@abstract The bundle identifier of your application.
+ *	@discussion The bundle identifier of your application. This key should
+ *   be unique for your application while there may be several applications
+ *   with the same GROWL_APP_NAME.
+ *   This key is optional.
+ */
+#define GROWL_APP_ID					XSTR("ApplicationId")
+/*!	@defined GROWL_APP_ICON
+ *	@abstract The image data for your application's icon.
+ *	@discussion Image data representing your application's icon. This may be
+ *	 superimposed on a notification icon as a badge, used as the notification
+ *	 icon when a notification-specific icon is not supplied, or ignored
+ *	 altogether, depending on the display. Must be in a format supported by
+ *	 NSImage, such as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF.
+ *
+ *	 Optional. Not supported by all display plugins.
+ */
+#define GROWL_APP_ICON					XSTR("ApplicationIcon")
+/*!	@defined GROWL_NOTIFICATIONS_DEFAULT
+ *	@abstract The array of notifications to turn on by default.
+ *	@discussion These are the names of the notifications that should be enabled
+ *	 by default when your application registers for the first time. If your
+ *	 application reregisters, Growl will look here for any new notification
+ *	 names found in GROWL_NOTIFICATIONS_ALL, but ignore any others.
+ */
+#define GROWL_NOTIFICATIONS_DEFAULT		XSTR("DefaultNotifications")
+/*!	@defined GROWL_NOTIFICATIONS_ALL
+ *	@abstract The array of all notifications your application can send.
+ *	@discussion These are the names of all of the notifications that your
+ *	 application may post. See GROWL_NOTIFICATION_NAME for a discussion of good
+ *	 notification names.
+ */
+#define GROWL_NOTIFICATIONS_ALL			XSTR("AllNotifications")
+/*! @defined GROWL_NOTIFICATIONS_HUMAN_READABLE_DESCRIPTIONS
+ *  @abstract A dictionary of human-readable names for your notifications.
+ *  @discussion By default, the Growl UI will display notifications by the names given in GROWL_NOTIFICATIONS_ALL
+ *  which correspond to the GROWL_NOTIFICATION_NAME. This dictionary specifies the human-readable name to display.
+ *  The keys of the dictionary are GROWL_NOTIFICATION_NAME strings; the objects are the human-readable versions.
+ *  For any GROWL_NOTIFICATION_NAME not specific in this dictionary, the GROWL_NOTIFICATION_NAME will be displayed.
+ *
+ *  This key is optional.
+ */
+#define GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES		XSTR("HumanReadableNames")
+/*! @defined GROWL_NOTIFICATIONS_DESCRIPTIONS
+*  @abstract A dictionary of descriptions of _when_ each notification occurs
+*  @discussion This is an NSDictionary whose keys are GROWL_NOTIFICATION_NAME strings and whose objects are
+*  descriptions of _when_ each notification occurs, such as "You received a new mail message" or
+*  "A file finished downloading".
+*
+*  This key is optional.
+*/
+#define GROWL_NOTIFICATIONS_DESCRIPTIONS		XSTR("NotificationDescriptions")
+
+/*!	@defined	GROWL_TICKET_VERSION
+ *	@abstract	The version of your registration ticket.
+ *	@discussion	Include this key in a ticket plist file that you put in your
+ *	 application bundle for auto-discovery. The current ticket version is 1.
+ */
+#define GROWL_TICKET_VERSION			XSTR("TicketVersion")
+// UserInfo Keys for Notifications
+#pragma mark UserInfo Keys for Notifications
+
+/*!	@group Notification userInfo keys */
+/*	@abstract	Keys for the userInfo dictionary of a GROWL_NOTIFICATION distributed notification.
+ *	@discussion The values of these keys describe the content of a Growl
+ *	 notification.
+ *
+ *	 Not all of these keys are supported by all displays. Only the name, title,
+ *	 and description of a notification are universal. Most of the built-in
+ *	 displays do support all of these keys, and most other visual displays
+ *	 probably will also. But, as of 0.6, the Log, MailMe, and Speech displays
+ *	 support only textual data.
+ */
+
+/*!	@defined GROWL_NOTIFICATION_NAME
+ *	@abstract The name of the notification.
+ *	@discussion The name of the notification. Note that if you do not define
+ *  GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES when registering your ticket originally this name
+ *  will the one displayed within the Growl preference pane and should be human-readable.
+ */
+#define GROWL_NOTIFICATION_NAME			XSTR("NotificationName")
+/*!	@defined GROWL_NOTIFICATION_TITLE
+ *	@abstract The title to display in the notification.
+ *	@discussion The title of the notification. Should be very brief.
+ *	 The title usually says what happened, e.g. "Download complete".
+ */
+#define GROWL_NOTIFICATION_TITLE		XSTR("NotificationTitle")
+/*!	@defined GROWL_NOTIFICATION_DESCRIPTION
+ *	@abstract The description to display in the notification.
+ *	@discussion The description should be longer and more verbose than the title.
+ *	 The description usually tells the subject of the action,
+ *	 e.g. "Growl-0.6.dmg downloaded in 5.02 minutes".
+ */
+#define GROWL_NOTIFICATION_DESCRIPTION  	XSTR("NotificationDescription")
+/*!	@defined GROWL_NOTIFICATION_ICON
+ *	@discussion Image data for the notification icon. Must be in a format
+ *	 supported by NSImage, such as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF.
+ *
+ *	 Optional. Not supported by all display plugins.
+ */
+#define GROWL_NOTIFICATION_ICON			XSTR("NotificationIcon")
+/*!	@defined GROWL_NOTIFICATION_APP_ICON
+ *	@discussion Image data for the application icon, in case GROWL_APP_ICON does
+ *	 not apply for some reason. Must be in a format supported by NSImage, such
+ *	 as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF.
+ *
+ *	 Optional. Not supported by all display plugins.
+ */
+#define GROWL_NOTIFICATION_APP_ICON		XSTR("NotificationAppIcon")
+/*!	@defined GROWL_NOTIFICATION_PRIORITY
+ *	@discussion The priority of the notification as an integer number from
+ *	 -2 to +2 (+2 being highest).
+ *
+ *	 Optional. Not supported by all display plugins.
+ */
+#define GROWL_NOTIFICATION_PRIORITY		XSTR("NotificationPriority")
+/*!	@defined GROWL_NOTIFICATION_STICKY
+ *	@discussion A Boolean number controlling whether the notification is sticky.
+ *
+ *	 Optional. Not supported by all display plugins.
+ */
+#define GROWL_NOTIFICATION_STICKY		XSTR("NotificationSticky")
+/*!	@defined GROWL_NOTIFICATION_CLICK_CONTEXT
+ *	@abstract Identifies which notification was clicked.
+ *	@discussion An identifier for the notification for clicking purposes.
+ *
+ *	 This will be passed back to the application when the notification is
+ *	 clicked. It must be plist-encodable (a data, dictionary, array, number, or
+ *	 string object), and it should be unique for each notification you post.
+ *	 A good click context would be a UUID string returned by NSProcessInfo or
+ *	 CFUUID.
+ *
+ *	 Optional. Not supported by all display plugins.
+ */
+#define GROWL_NOTIFICATION_CLICK_CONTEXT			XSTR("NotificationClickContext")
+
+/*!	@defined GROWL_DISPLAY_PLUGIN
+ *	@discussion The name of a display plugin which should be used for this notification.
+ *    Optional. If this key is not set or the specified display plugin does not
+ *    exist, the display plugin stored in the application ticket is used. This key
+ *    allows applications to use different default display plugins for their
+ *    notifications. The user can still override those settings in the preference
+ *    pane.
+ */
+#define GROWL_DISPLAY_PLUGIN				XSTR("NotificationDisplayPlugin")
+
+/*!	@defined GROWL_NOTIFICATION_IDENTIFIER
+ *	@abstract An identifier for the notification for coalescing purposes.
+ *   Notifications with the same identifier fall into the same class; only
+ *   the last notification of a class is displayed on the screen. If a
+ *   notification of the same class is currently being displayed, it is
+ *   replaced by this notification.
+ *
+ *	 Optional. Not supported by all display plugins.
+ */
+#define GROWL_NOTIFICATION_IDENTIFIER	XSTR("GrowlNotificationIdentifier")
+
+/*!	@defined GROWL_APP_PID
+ *	@abstract The process identifier of the process which sends this
+ *   notification. If this field is set, the application will only receive
+ *   clicked and timed out notifications which originate from this process.
+ *
+ *	 Optional.
+ */
+#define GROWL_APP_PID					XSTR("ApplicationPID")
+
+/*!	@defined GROWL_NOTIFICATION_PROGRESS
+*	@abstract If this key is set, it should contain a double value wrapped
+*     in a NSNumber which describes some sort of progress (from 0.0 to 100.0).
+*     If this is key is not set, no progress bar is shown.
+*
+*	 Optional. Not supported by all display plugins.
+*/
+#define GROWL_NOTIFICATION_PROGRESS		XSTR("NotificationProgress")
+
+// Notifications
+#pragma mark Notifications
+
+/*!	@group Notification names */
+/*	@abstract	Names of distributed notifications used by Growl.
+ *	@discussion	These are notifications used by applications (directly or
+ *	 indirectly) to interact with Growl, and by Growl for interaction between
+ *	 its components.
+ *
+ *	 Most of these should no longer be used in Growl 0.6 and later, in favor of
+ *	 Growl.framework's GrowlApplicationBridge APIs.
+ */
+
+/*!	@defined GROWL_APP_REGISTRATION
+ *	@abstract The distributed notification for registering your application.
+ *	@discussion This is the name of the distributed notification that can be
+ *	 used to register applications with Growl.
+ *
+ *	 The userInfo dictionary for this notification can contain these keys:
+ *	 <ul>
+ *	 	<li>GROWL_APP_NAME</li>
+ *	 	<li>GROWL_APP_ICON</li>
+ *	 	<li>GROWL_NOTIFICATIONS_ALL</li>
+ *	 	<li>GROWL_NOTIFICATIONS_DEFAULT</li>
+ *	 </ul>
+ *
+ *	 No longer recommended as of Growl 0.6. An alternate method of registering
+ *	 is to use Growl.framework's delegate system.
+ *	 See +[GrowlApplicationBridge setGrowlDelegate:] or Growl_SetDelegate for
+ *	 more information.
+ */
+#define GROWL_APP_REGISTRATION			XSTR("GrowlApplicationRegistrationNotification")
+/*!	@defined GROWL_APP_REGISTRATION_CONF
+ *	@abstract The distributed notification for confirming registration.
+ *	@discussion The name of the distributed notification sent to confirm the
+ *	 registration. Used by the Growl preference pane. Your application probably
+ *	 does not need to use this notification.
+ */
+#define GROWL_APP_REGISTRATION_CONF		XSTR("GrowlApplicationRegistrationConfirmationNotification")
+/*!	@defined GROWL_NOTIFICATION
+ *	@abstract The distributed notification for Growl notifications.
+ *	@discussion This is what it all comes down to. This is the name of the
+ *	 distributed notification that your application posts to actually send a
+ *	 Growl notification.
+ *
+ *	 The userInfo dictionary for this notification can contain these keys:
+ *	 <ul>
+ *	 	<li>GROWL_NOTIFICATION_NAME (required)</li>
+ *	 	<li>GROWL_NOTIFICATION_TITLE (required)</li>
+ *	 	<li>GROWL_NOTIFICATION_DESCRIPTION (required)</li>
+ *	 	<li>GROWL_NOTIFICATION_ICON</li>
+ *	 	<li>GROWL_NOTIFICATION_APP_ICON</li>
+ *	 	<li>GROWL_NOTIFICATION_PRIORITY</li>
+ *	 	<li>GROWL_NOTIFICATION_STICKY</li>
+ *	 	<li>GROWL_NOTIFICATION_CLICK_CONTEXT</li>
+ *	 	<li>GROWL_APP_NAME (required)</li>
+ *	 </ul>
+ *
+ *	 No longer recommended as of Growl 0.6. Three alternate methods of posting
+ *	 notifications are +[GrowlApplicationBridge notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:],
+ *	 Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext, and
+ *	 Growl_PostNotification.
+ */
+#define GROWL_NOTIFICATION				XSTR("GrowlNotification")
+/*!	@defined GROWL_SHUTDOWN
+*	@abstract The distributed notification name that tells Growl to shutdown.
+*	@discussion The Growl preference pane posts this notification when the
+*	 "Stop Growl" button is clicked.
+*/
+#define GROWL_SHUTDOWN					XSTR("GrowlShutdown")
+/*!	@defined GROWL_PING
+ *	@abstract A distributed notification to check whether Growl is running.
+ *	@discussion This is used by the Growl preference pane. If it receives a
+ *	 GROWL_PONG, the preference pane takes this to mean that Growl is running.
+ */
+#define GROWL_PING						XSTR("Honey, Mind Taking Out The Trash")
+/*!	@defined GROWL_PONG
+ *	@abstract The distributed notification sent in reply to GROWL_PING.
+ *	@discussion GrowlHelperApp posts this in reply to GROWL_PING.
+ */
+#define GROWL_PONG						XSTR("What Do You Want From Me, Woman")
+/*!	@defined GROWL_IS_READY
+ *	@abstract The distributed notification sent when Growl starts up.
+ *	@discussion GrowlHelperApp posts this when it has begin listening on all of
+ *	 its sources for new notifications. GrowlApplicationBridge (in
+ *	 Growl.framework), upon receiving this notification, reregisters using the
+ *	 registration dictionary supplied by its delegate.
+ */
+#define GROWL_IS_READY					XSTR("Lend Me Some Sugar; I Am Your Neighbor!")
+/*!	@defined GROWL_NOTIFICATION_CLICKED
+ *	@abstract The distributed notification sent when a supported notification is clicked.
+ *	@discussion When a Growl notification with a click context is clicked on by
+ *	 the user, Growl posts this distributed notification.
+ *	 The GrowlApplicationBridge responds to this notification by calling a
+ *	 callback in its delegate.
+ */
+#define GROWL_NOTIFICATION_CLICKED		XSTR("GrowlClicked!")
+#define GROWL_NOTIFICATION_TIMED_OUT	XSTR("GrowlTimedOut!")
+
+/*!	@group Other symbols */
+/* Symbols which don't fit into any of the other categories. */
+
+/*!	@defined GROWL_KEY_CLICKED_CONTEXT
+ *	@abstract Used internally as the key for the clickedContext passed over DNC.
+ *	@discussion This key is used in GROWL_NOTIFICATION_CLICKED, and contains the
+ *	 click context that was supplied in the original notification.
+ */
+#define GROWL_KEY_CLICKED_CONTEXT		XSTR("ClickedContext")
+/*!	@defined GROWL_REG_DICT_EXTENSION
+ *	@abstract The filename extension for registration dictionaries.
+ *	@discussion The GrowlApplicationBridge in Growl.framework registers with
+ *	 Growl by creating a file with the extension of .(GROWL_REG_DICT_EXTENSION)
+ *	 and opening it in the GrowlHelperApp. This happens whether or not Growl is
+ *	 running; if it was stopped, it quits immediately without listening for
+ *	 notifications.
+ */
+#define GROWL_REG_DICT_EXTENSION		XSTR("growlRegDict")
+
+
+#define GROWL_POSITION_PREFERENCE_KEY			@"GrowlSelectedPosition"
+
+#endif //ndef _GROWLDEFINES_H
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/GrowlDefinesInternal.h
@@ -0,0 +1,490 @@
+//
+//  GrowlDefinesInternal.h
+//  Growl
+//
+//  Created by Karl Adam on Mon May 17 2004.
+//  Copyright (c) 2004 the Growl Project. All rights reserved.
+//
+
+#ifndef _GROWL_GROWLDEFINESINTERNAL_H
+#define _GROWL_GROWLDEFINESINTERNAL_H
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef __OBJC__
+#define XSTR(x) (@x)
+#else /* !__OBJC__ */
+#define XSTR CFSTR
+#endif /* __OBJC__ */
+
+/*!	@header	GrowlDefinesInternal.h
+ *	@abstract	Defines internal Growl macros and types.
+ *  @ignore ATTRIBUTE_PACKED
+ *	@discussion	These constants are used both by GrowlHelperApp and by plug-ins.
+ *
+ *	 Notification keys (used in GrowlHelperApp, in GrowlApplicationBridge, and
+ *	 by applications that don't use GrowlApplicationBridge) are defined in
+ *	 GrowlDefines.h.
+ */
+
+/*!
+ * @defined NSInteger
+ * @abstract Typedef to int so Growl will compile on pre-10.5 SDKs.
+ */
+#ifndef NSINTEGER_DEFINED
+typedef int NSInteger;
+typedef unsigned int NSUInteger;
+#define NSINTEGER_DEFINED
+#endif
+
+/*!
+ * @defined CGFloat
+ * @abstract Typedef to float so Growl will compile on pre-10.5 SDKs.
+ */
+#ifndef CGFLOAT_DEFINED
+typedef float CGFloat;
+#define CGFLOAT_IS_DOUBLE 0
+#define CGFLOAT_DEFINED
+#endif
+
+/*!
+ * @defined GrowlCGFloatCeiling()
+ * @abstract Macro for the ceil() function that uses a different precision depending on the CPU architecture.
+ */
+/*!
+ * @defined GrowlCGFloatAbsoluteValue()
+ * @abstract Macro for the fabs() function that uses a different precision depending on the CPU architecture.
+ */
+/*!
+ * @defined GrowlCGFloatFloor()
+ * @abstract Macro for the floor() function that uses a different precision depending on the CPU architecture.
+ */
+#if CGFLOAT_IS_DOUBLE
+#define GrowlCGFloatCeiling(x) ceil(x)
+#define GrowlCGFloatAbsoluteValue(x) fabs(x)
+#define GrowlCGFloatFloor(x) floor(x)
+#else
+#define GrowlCGFloatCeiling(x) ceilf(x)
+#define GrowlCGFloatAbsoluteValue(x) fabsf(x)
+#define GrowlCGFloatFloor(x) floorf(x)
+#endif
+
+/*!	@defined	GROWL_TCP_PORT
+ *	@abstract	The TCP listen port for Growl notification servers.
+ */
+#define GROWL_TCP_PORT	23052
+
+/*!	@defined	GROWL_UDP_PORT
+ *	@abstract	The UDP listen port for Growl notification servers.
+ */
+#define GROWL_UDP_PORT	9887
+
+/*!	@defined	GROWL_PROTOCOL_VERSION
+ *	@abstract	The current version of the Growl network-notifications protocol (without encryption).
+ */
+#define GROWL_PROTOCOL_VERSION	1
+
+/*!	@defined	GROWL_PROTOCOL_VERSION_AES128
+*	@abstract	The current version of the Growl network-notifications protocol (with AES-128 encryption).
+*/
+#define GROWL_PROTOCOL_VERSION_AES128	2
+
+/*!	@defined	GROWL_TYPE_REGISTRATION
+ *	@abstract	The packet type of registration packets with MD5 authentication.
+ */
+#define GROWL_TYPE_REGISTRATION			0
+/*!	@defined	GROWL_TYPE_NOTIFICATION
+ *	@abstract	The packet type of notification packets with MD5 authentication.
+ */
+#define GROWL_TYPE_NOTIFICATION			1
+/*!	@defined	GROWL_TYPE_REGISTRATION_SHA256
+ *	@abstract	The packet type of registration packets with SHA-256 authentication.
+ */
+#define GROWL_TYPE_REGISTRATION_SHA256	2
+/*!	@defined	GROWL_TYPE_NOTIFICATION_SHA256
+ *	@abstract	The packet type of notification packets with SHA-256 authentication.
+ */
+#define GROWL_TYPE_NOTIFICATION_SHA256	3
+/*!	@defined	GROWL_TYPE_REGISTRATION_NOAUTH
+*	@abstract	The packet type of registration packets without authentication.
+*/
+#define GROWL_TYPE_REGISTRATION_NOAUTH	4
+/*!	@defined	GROWL_TYPE_NOTIFICATION_NOAUTH
+*	@abstract	The packet type of notification packets without authentication.
+*/
+#define GROWL_TYPE_NOTIFICATION_NOAUTH	5
+
+#define ATTRIBUTE_PACKED __attribute((packed))
+
+/*!	@struct	GrowlNetworkPacket
+ *	@abstract	This struct is a header common to all incoming Growl network
+ *	 packets which identifies the type and version of the packet.
+ */
+struct GrowlNetworkPacket {
+	unsigned char version;
+	unsigned char type;
+} ATTRIBUTE_PACKED;
+
+/*!
+ * @struct GrowlNetworkRegistration
+ * @abstract The format of a registration packet.
+ * @discussion A Growl client that wants to register with a Growl server sends
+ * a packet in this format.
+ * @field common The Growl packet header.
+ * @field appNameLen The name of the application that is registering.
+ * @field numAllNotifications The number of notifications in the list.
+ * @field numDefaultNotifications The number of notifications in the list that are enabled by default.
+ * @field data Variable-sized data.
+ */
+struct GrowlNetworkRegistration {
+	struct GrowlNetworkPacket common;
+	/*	This name is used both internally and in the Growl
+	 *	 preferences.
+	 *
+	 *	 The application name should remain stable between different versions
+	 *	 and incarnations of your application.
+	 *	 For example, "SurfWriter" is a good app name, whereas "SurfWriter 2.0"
+	 *	 and "SurfWriter Lite" are not.
+	 *
+	 *	 In addition to being unsigned, the application name length is in
+	 *	 network byte order.
+	 */
+	unsigned short appNameLen;
+	/*	These names are used both internally and in the Growl
+	 *	 preferences. For this reason, they should be human-readable.
+	 */
+	unsigned char numAllNotifications;
+
+	unsigned char numDefaultNotifications;
+	/*	The variable-sized data of a registration is:
+	 *	 - The application name, in UTF-8 encoding, for appNameLen bytes.
+	 *	 - The list of all notification names.
+	 *	 - The list of default notifications, as 8-bit unsigned indices into the list of all notifications.
+	 *	 - The MD5/SHA256 checksum of all the data preceding the checksum.
+	 *
+	 *	 Each notification name is encoded as:
+	 *	 - Length: two bytes, unsigned, network byte order.
+	 *	 - Name: As many bytes of UTF-8-encoded text as the length says.
+	 *	 And there are numAllNotifications of these.
+	 */
+	unsigned char data[];
+} ATTRIBUTE_PACKED;
+
+/*!
+ * @struct GrowlNetworkNotification
+ * @abstract The format of a notification packet.
+ * @discussion	A Growl client that wants to post a notification to a Growl
+ * server sends a packet in this format.
+ * @field common The Growl packet header.
+ * @field flags The priority number and the sticky bit.
+ * @field nameLen The length of the notification name.
+ * @field titleLen The length of the notification title.
+ * @field descriptionLen The length of the notification description.
+ * @field appNameLen The length of the application name.
+ * @field data Variable-sized data.
+ */
+struct GrowlNetworkNotification {
+	struct GrowlNetworkPacket common;
+	/*!
+	 * @struct GrowlNetworkNotificationFlags
+	 * @abstract Various flags.
+	 * @discussion This 16-bit packed structure contains the priority as a
+	 *  signed 3-bit integer from -2 to +2, and the sticky flag as a single bit.
+	 *  The high 12 bits of the structure are reserved for future use.
+	 * @field reserved reserved for future use.
+	 * @field priority the priority as a signed 3-bit integer from -2 to +2.
+	 * @field sticky the sticky flag.
+	 */
+	struct GrowlNetworkNotificationFlags {
+#ifdef __BIG_ENDIAN__
+		unsigned reserved: 12;
+		signed   priority: 3;
+		unsigned sticky:   1;
+#else
+		unsigned sticky:   1;
+		signed   priority: 3;
+		unsigned reserved: 12;
+#endif
+	} ATTRIBUTE_PACKED flags; //size = 16 (12 + 3 + 1)
+
+	/*	In addition to being unsigned, the notification name length
+	 *	 is in network byte order.
+	 */
+	unsigned short nameLen;
+	/*	@discussion	In addition to being unsigned, the title length is in
+	 *	 network byte order.
+	 */
+	unsigned short titleLen;
+	/*	In addition to being unsigned, the description length is in
+	 *	 network byte order.
+	 */
+	unsigned short descriptionLen;
+	/*	In addition to being unsigned, the application name length
+	 *	 is in network byte order.
+	 */
+	unsigned short appNameLen;
+	/*	The variable-sized data of a notification is:
+	 *	 - Notification name, in UTF-8 encoding, for nameLen bytes.
+	 *	 - Title, in UTF-8 encoding, for titleLen bytes.
+	 *	 - Description, in UTF-8 encoding, for descriptionLen bytes.
+	 *	 - Application name, in UTF-8 encoding, for appNameLen bytes.
+	 *	 - The MD5/SHA256 checksum of all the data preceding the checksum.
+	 */
+	unsigned char data[];
+} ATTRIBUTE_PACKED;
+
+/*!	@defined	GrowlEnabledKey
+ *	@abstract	Preference key controlling whether Growl is enabled.
+ *	@discussion	If this is false, then when GrowlHelperApp is launched to open
+ *	 a Growl registration dictionary file, GrowlHelperApp will quit when it has
+ *	 finished processing the file instead of listening for notifications.
+ */
+#define GrowlEnabledKey					XSTR("GrowlEnabled")
+
+/*!	@defined	GROWL_SCREENSHOT_MODE
+ *	@abstract	Preference and notification key controlling whether to save a screenshot of the notification.
+ *	@discussion	This is for GHA's private usage. If your application puts this
+ *	 key into a notification dictionary, GHA will clobber it. This key is only
+ *	 allowed in the notification dictionaries GHA passes to displays.
+ *
+ *	 If this key contains an object whose boolValue is not NO, the display is
+ *	 asked to save a screenshot of the notification to
+ *	 ~/Library/Application\ Support/Growl/Screenshots.
+ */
+#define GROWL_SCREENSHOT_MODE			XSTR("ScreenshotMode")
+
+/*!	@defined	GROWL_APP_LOCATION
+ *	@abstract	The location of this application.
+ *	@discussion	Contains either the POSIX path to the application, or a file-data dictionary (as used by the Dock).
+ *	 contains the file's alias record and its pathname.
+ */
+#define GROWL_APP_LOCATION				XSTR("AppLocation")
+
+/*!	@defined	GROWL_REMOTE_ADDRESS
+ *	@abstract	The address of the host who sent this notification/registration.
+ *	@discussion	Contains an NSData with the address of the remote host who
+ *    sent this notification/registration.
+ */
+#define GROWL_REMOTE_ADDRESS			XSTR("RemoteAddress")
+
+/*!
+ *	@defined    GROWL_PREFPANE_BUNDLE_IDENTIFIER
+ *	@discussion The bundle identifier for the Growl preference pane.
+ */
+#define GROWL_PREFPANE_BUNDLE_IDENTIFIER		XSTR("com.growl.prefpanel")
+/*!
+ *	@defined    GROWL_HELPERAPP_BUNDLE_IDENTIFIER
+ *	@discussion The bundle identifier for the Growl background application (GrowlHelperApp).
+ */
+#define GROWL_HELPERAPP_BUNDLE_IDENTIFIER	XSTR("com.Growl.GrowlHelperApp")
+
+/*!
+ *	@defined    GROWL_PREFPANE_NAME
+ *	@discussion The file name of the Growl preference pane.
+ */
+#define GROWL_PREFPANE_NAME						XSTR("Growl.prefPane")
+#define PREFERENCE_PANES_SUBFOLDER_OF_LIBRARY	XSTR("PreferencePanes")
+#define PREFERENCE_PANE_EXTENSION				XSTR("prefPane")
+
+//plug-in bundle filename extensions
+#define GROWL_PLUGIN_EXTENSION                  XSTR("growlPlugin")
+#define GROWL_PATHWAY_EXTENSION                 XSTR("growlPathway")
+#define GROWL_VIEW_EXTENSION					XSTR("growlView")
+#define GROWL_STYLE_EXTENSION					XSTR("growlStyle")
+#define GROWL_PATHEXTENSION_TICKET				XSTR("growlTicket")
+
+/* --- These following macros are intended for plug-ins --- */
+
+/*!	@function    SYNCHRONIZE_GROWL_PREFS
+ *	@abstract    Synchronizes Growl prefs so they're up-to-date.
+ *	@discussion  This macro is intended for use by GrowlHelperApp and by
+ *	 plug-ins (when the prefpane is selected).
+ */
+#define SYNCHRONIZE_GROWL_PREFS() CFPreferencesAppSynchronize(CFSTR("com.Growl.GrowlHelperApp"))
+
+/*!	@function    UPDATE_GROWL_PREFS
+ *	@abstract    Tells GrowlHelperApp to update its prefs.
+ *	@discussion  This macro is intended for use by plug-ins.
+ *	 It sends a notification to tell GrowlHelperApp to update its preferences.
+ */
+#define UPDATE_GROWL_PREFS() do { \
+	SYNCHRONIZE_GROWL_PREFS(); \
+	CFStringRef _key = CFSTR("pid"); \
+	int pid = getpid(); \
+	CFNumberRef _value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid); \
+	CFDictionaryRef userInfo = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&_key, (const void **)&_value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); \
+	CFRelease(_value); \
+	CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(), \
+										 CFSTR("GrowlPreferencesChanged"), \
+										 CFSTR("GrowlUserDefaults"), \
+										 userInfo, false); \
+	CFRelease(userInfo); \
+	} while(0)
+
+/*!	@function    READ_GROWL_PREF_VALUE
+ *	@abstract    Reads the given pref value from the plug-in's preferences.
+ *	@discussion  This macro is intended for use by plug-ins. It reads the value for the
+ *	 given key from the plug-in's preferences (which are stored in a dictionary inside of
+ *	 GrowlHelperApp's prefs).
+ *	@param	key	The preference key to read the value of.
+ *	@param	domain	The bundle ID of the plug-in.
+ *	@param	type	The type of the result expected.
+ *	@param	result	A pointer to an id. Set to the value if exists, left unchanged if not.
+ *
+ *	 If the value is set, you are responsible for releasing it.
+ */
+#define READ_GROWL_PREF_VALUE(key, domain, type, result) do {\
+	CFDictionaryRef prefs = (CFDictionaryRef)CFPreferencesCopyAppValue((CFStringRef)domain, \
+																		CFSTR("com.Growl.GrowlHelperApp")); \
+	if (prefs) {\
+		if (CFDictionaryContainsKey(prefs, key)) {\
+			*result = (type)CFDictionaryGetValue(prefs, key); \
+			CFRetain(*result); \
+		} \
+		CFRelease(prefs); } \
+	} while(0)
+
+/*!	@function    WRITE_GROWL_PREF_VALUE
+ *	@abstract    Writes the given pref value to the plug-in's preferences.
+ *	@discussion  This macro is intended for use by plug-ins. It writes the given
+ *	 value to the plug-in's preferences.
+ *	@param	key	The preference key to write the value of.
+ *	@param	value	The value to write to the preferences. It should be either a
+ *	 CoreFoundation type or toll-free bridged with one.
+ *	@param	domain	The bundle ID of the plug-in.
+ */
+#define WRITE_GROWL_PREF_VALUE(key, value, domain) do {\
+	CFDictionaryRef staticPrefs = (CFDictionaryRef)CFPreferencesCopyAppValue((CFStringRef)domain, \
+																			 CFSTR("com.Growl.GrowlHelperApp")); \
+	CFMutableDictionaryRef prefs; \
+	if (staticPrefs == NULL) {\
+		prefs = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); \
+	} else {\
+		prefs = CFDictionaryCreateMutableCopy(NULL, 0, staticPrefs); \
+		CFRelease(staticPrefs); \
+	}\
+	CFDictionarySetValue(prefs, key, value); \
+	CFPreferencesSetAppValue((CFStringRef)domain, prefs, CFSTR("com.Growl.GrowlHelperApp")); \
+	CFRelease(prefs); } while(0)
+
+/*!	@function    READ_GROWL_PREF_BOOL
+ *	@abstract    Reads the given Boolean from the plug-in's preferences.
+ *	@discussion  This is a wrapper around READ_GROWL_PREF_VALUE() intended for
+ *	 use with Booleans.
+ *	@param	key	The preference key to read the Boolean from.
+ *	@param	domain	The bundle ID of the plug-in.
+ *	@param	result	A pointer to a Boolean type. Left unchanged if the value doesn't exist.
+ */
+#define READ_GROWL_PREF_BOOL(key, domain, result) do {\
+	CFBooleanRef boolValue = NULL; \
+	READ_GROWL_PREF_VALUE(key, domain, CFBooleanRef, &boolValue); \
+	if (boolValue) {\
+		*result = CFBooleanGetValue(boolValue); \
+		CFRelease(boolValue); \
+	} } while(0)
+
+/*!	@function    WRITE_GROWL_PREF_BOOL
+ *	@abstract    Writes the given Boolean to the plug-in's preferences.
+ *	@discussion  This is a wrapper around WRITE_GROWL_PREF_VALUE() intended for
+ *	 use with Booleans.
+ *	@param	key	The preference key to write the Boolean for.
+ *	@param	value	The Boolean value to write to the preferences.
+ *	@param	domain	The bundle ID of the plug-in.
+ */
+#define WRITE_GROWL_PREF_BOOL(key, value, domain) do {\
+	WRITE_GROWL_PREF_VALUE(key, value ? kCFBooleanTrue : kCFBooleanFalse, domain); } while(0)
+
+/*!	@function    READ_GROWL_PREF_INT
+ *	@abstract    Reads the given integer from the plug-in's preferences.
+ *	@discussion  This is a wrapper around READ_GROWL_PREF_VALUE() intended for
+ *	 use with integers.
+ *	@param	key	The preference key to read the integer from.
+ *	@param	domain	The bundle ID of the plug-in.
+ *	@param	result	A pointer to an integer. Leaves unchanged if the value doesn't exist.
+ */
+#define READ_GROWL_PREF_INT(key, domain, result) do {\
+	CFNumberRef intValue = NULL; \
+	READ_GROWL_PREF_VALUE(key, domain, CFNumberRef, &intValue); \
+	if (intValue) {\
+		CFNumberGetValue(intValue, kCFNumberIntType, result); \
+		CFRelease(intValue); \
+	} } while(0)
+
+/*!	@function    WRITE_GROWL_PREF_INT
+ *	@abstract    Writes the given integer to the plug-in's preferences.
+ *	@discussion  This is a wrapper around WRITE_GROWL_PREF_VALUE() intended for
+ *	 use with integers.
+ *	@param	key	The preference key to write the integer for.
+ *	@param	value	The integer value to write to the preferences.
+ *	@param	domain	The bundle ID of the plug-in.
+ */
+#define WRITE_GROWL_PREF_INT(key, value, domain) do {\
+	CFNumberRef intValue = CFNumberCreate(NULL, kCFNumberIntType, &value); \
+	WRITE_GROWL_PREF_VALUE(key, intValue, domain); \
+	CFRelease(intValue); } while(0)
+
+/*!	@function    READ_GROWL_PREF_FLOAT
+ *	@abstract    Reads the given float from the plug-in's preferences.
+ *	@discussion  This is a wrapper around READ_GROWL_PREF_VALUE() intended for
+ *	 use with floats.
+ *	@param	key	The preference key to read the float from.
+ *	@param	domain	The bundle ID of the plug-in.
+ *	@param	result	A pointer to a float. Leaves unchanged if the value doesn't exist.
+ */
+#ifdef __LP64__
+#define READ_GROWL_PREF_FLOAT(key, domain, result) do {\
+	CFNumberRef floatValue = NULL; \
+	READ_GROWL_PREF_VALUE(key, domain, CFNumberRef, &floatValue); \
+	if (floatValue) {\
+		CFNumberGetValue(floatValue, kCFNumberCGFloatType, result); \
+		CFRelease(floatValue); \
+	} } while(0)
+#else
+#define READ_GROWL_PREF_FLOAT(key, domain, result) do {\
+	CFNumberRef floatValue = NULL; \
+	READ_GROWL_PREF_VALUE(key, domain, CFNumberRef, &floatValue); \
+	if (floatValue) {\
+		CFNumberGetValue(floatValue, kCFNumberFloatType, result); \
+		CFRelease(floatValue); \
+	} } while(0)
+#endif
+
+/*!	@function    WRITE_GROWL_PREF_FLOAT
+ *	@abstract    Writes the given float to the plug-in's preferences.
+ *	@discussion  This is a wrapper around WRITE_GROWL_PREF_VALUE() intended for
+ *	 use with floats.
+ *	@param	key	The preference key to write the float for.
+ *	@param	value	The float value to write to the preferences.
+ *	@param	domain	The bundle ID of the plug-in.
+ */
+#ifdef __LP64__
+#define WRITE_GROWL_PREF_FLOAT(key, value, domain) do {\
+	CFNumberRef floatValue = CFNumberCreate(NULL, kCFNumberCGFloatType, &value); \
+	WRITE_GROWL_PREF_VALUE(key, floatValue, domain); \
+	CFRelease(floatValue); } while(0)
+#else
+#define WRITE_GROWL_PREF_FLOAT(key, value, domain) do {\
+	CFNumberRef floatValue = CFNumberCreate(NULL, kCFNumberFloatType, &value); \
+	WRITE_GROWL_PREF_VALUE(key, floatValue, domain); \
+	CFRelease(floatValue); } while(0)
+#endif
+
+
+/*!	@defined	GROWL_CLOSE_ALL_NOTIFICATIONS
+ *	@abstract	Notification to close all Growl notifications
+ *	@discussion	Should be posted to the default notification center when a close widget is option+clicked.
+ *    All notifications should close in response. 
+ */
+#define GROWL_CLOSE_ALL_NOTIFICATIONS XSTR("GrowlCloseAllNotifications")
+
+#pragma mark Small utilities
+
+/*!
+ * @defined FLOAT_EQ(x,y)
+ * @abstract Compares two floats.
+ */
+#define FLOAT_EQ(x,y) (((y - FLT_EPSILON) < x) && (x < (y + FLT_EPSILON)))
+
+#endif //ndef _GROWL_GROWLDEFINESINTERNAL_H
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/GrowlPathUtilities.h
@@ -0,0 +1,171 @@
+//
+//  GrowlPathUtilities.h
+//  Growl
+//
+//  Created by Ingmar Stein on 17.04.05.
+//  Copyright 2005-2006 The Growl Project. All rights reserved.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+/*we can't typedef the enum, because then NSSearchPathDirectory constants
+ *	cannot be used in GrowlSearchPathDirectory arguments/variables without a
+ *	compiler warning (because NSSearchPathDirectory and GrowlSearchPathDirectory
+ *	would then be different types).
+ */
+typedef int GrowlSearchPathDirectory;
+enum {
+	//Library/Application\ Support/Growl
+	GrowlSupportDirectory = 0x10000,
+	//all other directory constants refer to subdirectories of Growl Support.
+	GrowlScreenshotsDirectory,
+	GrowlTicketsDirectory,
+	GrowlPluginsDirectory,
+};
+typedef NSSearchPathDomainMask GrowlSearchPathDomainMask; //consistency
+
+@interface GrowlPathUtilities : NSObject {
+
+}
+
+#pragma mark Bundles
+
+/*!	@method	growlPrefPaneBundle
+ *	@abstract	Returns the Growl preference pane bundle.
+ *	@discussion	First, attempts to retrieve the bundle for a running
+ *	 GrowlHelperApp process using <code>runningHelperAppBundle</code>, and if
+ *	 that was successful, returns the .prefpane bundle that contains it (if any).
+ *	Then, if that failed, searches all installed preference panes for the Growl
+ *	 preference pane.
+ *	@result	The <code>NSBundle</code> for the Growl preference pane if it is
+ *	 installed; <code>nil</code> otherwise.
+ */
++ (NSBundle *) growlPrefPaneBundle;
+/*!	@method	helperAppBundle
+ *	@abstract	Returns the GrowlHelperApp bundle.
+ *	@discussion	First, attempts to retrieve the bundle for a running
+ *	 GrowlHelperApp process using <code>runningHelperAppBundle</code>, and
+ *	 returns that if it was successful.
+ *	Then, if it wasn't, searches for a Growl preference pane, and, if one is
+ *	 installed, returns the GrowlHelperApp bundle inside it.
+ *	@result	The <code>NSBundle</code> for GrowlHelperApp if it is present;
+ *	 <code>nil</code> otherwise.
+ */
++ (NSBundle *) helperAppBundle;
+
+/*!	@method	runningHelperAppBundle
+ *	@abstract	Returns the bundle for the running GrowlHelperApp process.
+ *	@discussion	If GrowlHelperApp is running, returns an NSBundle for the .app 
+ *	 bundle it was loaded from.
+ *	If GrowlHelperApp is not running, returns <code>nil</code>.
+ *	@result	The <code>NSBundle</code> for GrowlHelperApp if it is running;
+ *	 <code>nil</code> otherwise.
+ */
++ (NSBundle *) runningHelperAppBundle;
+
+#pragma mark Directories
+
+/*!	@method	searchPathForDirectory:inDomains:mustBeWritable:
+ *	@abstract	Returns an array of absolute paths to a given directory.
+ *	@discussion	This method returns an array of all the directories of a given
+ *	 type that exist (and, if <code>flag</code> is <code>YES</code>, are
+ *	 writable). If no directories match this criteria, a valid (but empty)
+ *	 array is returned.
+ *
+ *	 Unlike the <code>NSSearchPathForDirectoriesInDomains</code> function in
+ *	 Foundation, this method does not allow you to specify whether tildes are
+ *	 expanded: they will always be expanded.
+ *	@result	An array of zero or more absolute paths.
+ */
++ (NSArray *) searchPathForDirectory:(GrowlSearchPathDirectory) directory inDomains:(GrowlSearchPathDomainMask) domainMask mustBeWritable:(BOOL)flag;
+/*!	@method	searchPathForDirectory:inDomains:
+ *	@abstract	Returns an array of absolute paths to a given directory.
+ *	@discussion	This method returns an array of all the directories of a given
+ *	 type that exist. They need not be writable.
+ *
+ *	 Unlike the <code>NSSearchPathForDirectoriesInDomains</code> function in
+ *	 Foundation, this method does not allow you to specify whether tildes are
+ *	 expanded: they will always be expanded.
+ *	@result	An array of zero or more absolute paths.
+ */
++ (NSArray *) searchPathForDirectory:(GrowlSearchPathDirectory) directory inDomains:(GrowlSearchPathDomainMask) domainMask;
+
+/*! @method	growlSupportDirectory
+ *	@abstract	Returns the path for Growl's folder inside Application Support.
+ *	@discussion	This method creates the folder if it does not already exist.
+ *	@result	The path to Growl's support directory.
+ */
++ (NSString *) growlSupportDirectory;
+
+/*!	@method	screenshotsDirectory
+ *	@abstract	Returns the directory where screenshots are to be stored.
+ *	@discussion	The default location of this directory is
+ *	 $HOME/Library/Application\ Support/Growl/Screenshots. This method creates
+ *	 the folder if it does not already exist.
+ *	@result	The absolute path to the screenshot directory.
+ */
++ (NSString *) screenshotsDirectory;
+
+/*!	@method	ticketsDirectory
+ *	@abstract	Returns the directory where tickets are to be saved.
+ *	@discussion	The default location of this directory is
+ *	 $HOME/Library/Application\ Support/Growl/Tickets. This method creates
+ *	 the folder if it does not already exist.
+ *	@result	The absolute path to the ticket directory.
+ */
++ (NSString *) ticketsDirectory;
+
+#pragma mark Screenshot names
+
+/*!	@method	nextScreenshotName
+ *	@abstract	Returns the name you should use for the next screenshot.
+ *	@discussion	Names returned by this method are currently in the format
+ *	 'Screenshot N', where N starts at 1 and continues indefinitely. Note the
+ *	 lack of a filename extension: you append it yourself.
+ *
+ *	 The name returned by this method is guaranteed to not exist with any
+ *	 filename extension. This is intentional: it would be confusing for the
+ *	 user if the fifth screenshot were assigned the name 'Screenshot 1' simply
+ *	 because the previous four screenshots had a different filename extension.
+ *
+ *	 Calling this method is the same as calling
+ *	 <code>nextScreenshotNameInDirectory:</code> with a directory of
+ *	 <code>nil</code>.
+ *	@result	A valid, non-existing, serial filename for a screenshot.
+ */
++ (NSString *) nextScreenshotName;
+/*!	@method	nextScreenshotNameInDirectory:
+ *	@abstract	Returns the name you should use for the next screenshot in a directory.
+ *	@discussion	Names returned by this method are currently in the format
+ *	 'Screenshot N', where N starts at 1 and continues indefinitely. Note the
+ *	 lack of a filename extension: you append it yourself. Note also that the
+ *	 directory is not included as a prefix on the result.
+ *
+ *	 The name returned by this method is guaranteed to not exist in the given
+ *	 directory with any filename extension. This is intentional: it would be
+ *	 confusing for the user if the fifth screenshot were assigned the name
+ *	 'Screenshot 1' simply because the previous four screenshots had a
+ *	 different filename extension.
+ *	@result	A valid, non-existing, serial filename for a screenshot.
+ */
++ (NSString *) nextScreenshotNameInDirectory:(NSString *) dirName;
+
+#pragma mark Tickets
+
+/*!	@method	defaultSavePathForTicketWithApplicationName:
+ *	@abstract	Returns an absolute path that can be used for saving a ticket.
+ *	@discussion	When called with an application name, ".ticket" is appended to
+ *	 it, and the result is appended to the absolute path to the ticket
+ *	 directory. When called with <code>nil</code>, the ticket directory itself
+ *	 is returned.
+ *
+ *	 For the purpose of this method, 'the ticket directory' refers to the first
+ *	 writable directory returned by
+ *	 <code>+searchPathForDirectory:inDomains:</code>. If there is no writable
+ *	 directory, this method returns <code>nil</code>.
+ *	@param	The application name for the ticket, or <code>nil</code>.
+ *	@result	The absolute path to a ticket file, or the ticket directory where a
+ *	 ticket file can be saved.
+ */
++ (NSString *) defaultSavePathForTicketWithApplicationName:(NSString *) appName;
+
+@end
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/GrowlPathUtilities.m
@@ -0,0 +1,380 @@
+//
+//  GrowlPathUtil.m
+//  Growl
+//
+//  Created by Ingmar Stein on 17.04.05.
+//  Copyright 2005-2006 The Growl Project. All rights reserved.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+#import <Cocoa/Cocoa.h>
+
+#import "GrowlPathUtilities.h"
+#import "GrowlDefinesInternal.h"
+
+static NSBundle *helperAppBundle;
+static NSBundle *prefPaneBundle;
+
+#define NAME_OF_SCREENSHOTS_DIRECTORY           @"Screenshots"
+#define NAME_OF_TICKETS_DIRECTORY               @"Tickets"
+#define NAME_OF_PLUGINS_DIRECTORY               @"Plugins"
+
+@implementation GrowlPathUtilities
+
+#pragma mark Bundles
+
+//Searches the process list (as yielded by GetNextProcess) for a process with the given bundle identifier.
+//Returns the oldest matching process.
++ (NSBundle *) bundleForProcessWithBundleIdentifier:(NSString *)identifier
+{
+
+restart:;
+	OSStatus err;
+	NSBundle *bundle = nil;
+	struct ProcessSerialNumber psn = { 0, 0 };
+	UInt32 oldestProcessLaunchDate = UINT_MAX;
+
+	while ((err = GetNextProcess(&psn)) == noErr) {
+		struct ProcessInfoRec info = { .processInfoLength = (UInt32)sizeof(struct ProcessInfoRec) };
+		err = GetProcessInformation(&psn, &info);
+		if (err == noErr) {
+			//Compare the launch dates first, since it's cheaper than comparing bundle IDs.
+			if (info.processLaunchDate < oldestProcessLaunchDate) {
+				//This one is older (fewer ticks since startup), so this is our current prospect to be the result.
+				NSDictionary *dict = (NSDictionary *)ProcessInformationCopyDictionary(&psn, kProcessDictionaryIncludeAllInformationMask);
+				
+				if (dict) {
+					CFMakeCollectable(dict);
+					pid_t pid = 0;
+					GetProcessPID(&psn, &pid);
+					if ([[dict objectForKey:(NSString *)kCFBundleIdentifierKey] isEqualToString:identifier]) {
+						NSString *bundlePath = [dict objectForKey:@"BundlePath"];
+						if (bundlePath) {
+							bundle = [NSBundle bundleWithPath:bundlePath];
+							oldestProcessLaunchDate = info.processLaunchDate;
+						}
+					}
+
+					[dict release];
+				} else {
+					//ProcessInformationCopyDictionary returning NULL probably means that the process disappeared out from under us (i.e., exited) in between GetProcessInformation and ProcessInformationCopyDictionary. Start over.
+					goto restart;
+				}
+			}
+		} else {
+			if (err != noErr) {
+				//Unexpected failure of GetProcessInformation (Process Manager got confused?). Assume severe breakage and bail.
+				NSLog(@"Couldn't get information about process %lu,%lu: GetProcessInformation returned %i/%s", psn.highLongOfPSN, psn.lowLongOfPSN, err, GetMacOSStatusCommentString(err));
+				err = noErr; //So our NSLog for GetNextProcess doesn't complain. (I wish I had Python's while..else block.)
+				break;
+			} else {
+				//Process disappeared out from under us (i.e., exited) in between GetNextProcess and GetProcessInformation. Start over.
+				goto restart;
+			}
+		}
+	}
+	if (err != procNotFound) {
+		NSLog(@"%s: GetNextProcess returned %i/%s", __PRETTY_FUNCTION__, err, GetMacOSStatusCommentString(err));
+	}
+
+	return bundle;
+}
+
+//Obtains the bundle for the active GrowlHelperApp process. Returns nil if there is no such process.
++ (NSBundle *) runningHelperAppBundle {
+	return [self bundleForProcessWithBundleIdentifier:GROWL_HELPERAPP_BUNDLE_IDENTIFIER];
+}
+
++ (NSBundle *) growlPrefPaneBundle {
+	NSArray			*librarySearchPaths;
+	NSString		*path;
+	NSString		*bundleIdentifier;
+	NSEnumerator	*searchPathEnumerator;
+	NSBundle		*bundle;
+
+	if (prefPaneBundle)
+		return prefPaneBundle;
+
+	prefPaneBundle = [NSBundle bundleWithIdentifier:GROWL_PREFPANE_BUNDLE_IDENTIFIER];
+ 	if (prefPaneBundle)
+		return prefPaneBundle;
+
+	//If GHA is running, the prefpane bundle is the bundle that contains it.
+	NSBundle *runningHelperAppBundle = [self runningHelperAppBundle];
+	NSString *runningHelperAppBundlePath = [runningHelperAppBundle bundlePath];
+	//GHA in Growl.prefPane/Contents/Resources/
+	NSString *possiblePrefPaneBundlePath1 = [runningHelperAppBundlePath stringByDeletingLastPathComponent];
+	//GHA in Growl.prefPane/ (hypothetical)
+	NSString *possiblePrefPaneBundlePath2 = [[possiblePrefPaneBundlePath1 stringByDeletingLastPathComponent] stringByDeletingLastPathComponent];
+	if ([[[possiblePrefPaneBundlePath1 pathExtension] lowercaseString] isEqualToString:@"prefpane"]) {
+		prefPaneBundle = [NSBundle bundleWithPath:possiblePrefPaneBundlePath1];
+		if (prefPaneBundle)
+			return prefPaneBundle;
+	}
+	if ([[[possiblePrefPaneBundlePath2 pathExtension] lowercaseString] isEqualToString:@"prefpane"]) {
+		prefPaneBundle = [NSBundle bundleWithPath:possiblePrefPaneBundlePath2];
+		if (prefPaneBundle)
+			return prefPaneBundle;
+	}
+	
+	static const unsigned bundleIDComparisonFlags = NSCaseInsensitiveSearch | NSBackwardsSearch;
+
+	NSFileManager *fileManager = [NSFileManager defaultManager];
+
+	//Find Library directories in all domains except /System (as of Panther, that's ~/Library, /Library, and /Network/Library)
+	librarySearchPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask & ~NSSystemDomainMask, YES);
+
+	/*First up, we'll look for Growl.prefPane, and if it exists, check whether
+	 *	it is our prefPane.
+	 *This is much faster than having to enumerate all preference panes, and
+	 *	can drop a significant amount of time off this code.
+	 */
+	searchPathEnumerator = [librarySearchPaths objectEnumerator];
+	while ((path = [searchPathEnumerator nextObject])) {
+		path = [path stringByAppendingPathComponent:PREFERENCE_PANES_SUBFOLDER_OF_LIBRARY];
+		path = [path stringByAppendingPathComponent:GROWL_PREFPANE_NAME];
+
+		if ([fileManager fileExistsAtPath:path]) {
+			bundle = [NSBundle bundleWithPath:path];
+
+			if (bundle) {
+				bundleIdentifier = [bundle bundleIdentifier];
+
+				if (bundleIdentifier && ([bundleIdentifier compare:GROWL_PREFPANE_BUNDLE_IDENTIFIER options:bundleIDComparisonFlags] == NSOrderedSame)) {
+					prefPaneBundle = bundle;
+					return prefPaneBundle;
+				}
+			}
+		}
+	}
+
+	/*Enumerate all installed preference panes, looking for the Growl prefpane
+	 *	bundle identifier and stopping when we find it.
+	 *Note that we check the bundle identifier because we should not insist
+	 *	that the user not rename his preference pane files, although most users
+	 *	of course will not.  If the user wants to mutilate the Info.plist file
+	 *	inside the bundle, he/she deserves to not have a working Growl
+	 *	installation.
+	 */
+	searchPathEnumerator = [librarySearchPaths objectEnumerator];
+	while ((path = [searchPathEnumerator nextObject])) {
+		NSString				*bundlePath;
+		NSDirectoryEnumerator   *bundleEnum;
+
+		path = [path stringByAppendingPathComponent:PREFERENCE_PANES_SUBFOLDER_OF_LIBRARY];
+		bundleEnum = [fileManager enumeratorAtPath:path];
+
+		while ((bundlePath = [bundleEnum nextObject])) {
+			if ([[bundlePath pathExtension] isEqualToString:PREFERENCE_PANE_EXTENSION]) {
+				bundle = [NSBundle bundleWithPath:[path stringByAppendingPathComponent:bundlePath]];
+
+				if (bundle) {
+					bundleIdentifier = [bundle bundleIdentifier];
+
+					if (bundleIdentifier && ([bundleIdentifier compare:GROWL_PREFPANE_BUNDLE_IDENTIFIER options:bundleIDComparisonFlags] == NSOrderedSame)) {
+						prefPaneBundle = bundle;
+						return prefPaneBundle;
+					}
+				}
+
+				[bundleEnum skipDescendents];
+			}
+		}
+	}
+
+	return nil;
+}
+
++ (NSBundle *) helperAppBundle {
+	if (!helperAppBundle) {
+		helperAppBundle = [self runningHelperAppBundle];
+		if (!helperAppBundle) {
+			//look in the prefpane bundle.
+			NSBundle *bundle = [GrowlPathUtilities growlPrefPaneBundle];
+			NSString *helperAppPath = [bundle pathForResource:@"GrowlHelperApp" ofType:@"app"];
+			helperAppBundle = [NSBundle bundleWithPath:helperAppPath];
+		}
+	}
+	return helperAppBundle;
+}
+
+#pragma mark -
+#pragma mark Directories
+
++ (NSArray *) searchPathForDirectory:(GrowlSearchPathDirectory) directory inDomains:(GrowlSearchPathDomainMask) domainMask mustBeWritable:(BOOL)flag {
+	if (directory < GrowlSupportDirectory) {
+		NSArray *searchPath = NSSearchPathForDirectoriesInDomains(directory, domainMask, /*expandTilde*/ YES);
+		if (!flag)
+			return searchPath;
+		else {
+			//flag is not NO: exclude non-writable directories.
+			NSMutableArray *result = [NSMutableArray arrayWithCapacity:[searchPath count]];
+			NSFileManager *mgr = [NSFileManager defaultManager];
+
+			NSEnumerator *searchPathEnum = [searchPath objectEnumerator];
+			NSString *dir;
+			while ((dir = [searchPathEnum nextObject])) {
+				if ([mgr isWritableFileAtPath:dir])
+					[result addObject:dir];
+			}
+
+			return result;
+		}
+	} else {
+		//determine what to append to each Application Support folder.
+		NSString *subpath = nil;
+		switch (directory) {
+			case GrowlSupportDirectory:
+				//do nothing.
+				break;
+
+			case GrowlScreenshotsDirectory:
+				subpath = NAME_OF_SCREENSHOTS_DIRECTORY;
+				break;
+
+			case GrowlTicketsDirectory:
+				subpath = NAME_OF_TICKETS_DIRECTORY;
+				break;
+
+			case GrowlPluginsDirectory:
+				subpath = NAME_OF_PLUGINS_DIRECTORY;
+				break;
+
+			default:
+				NSLog(@"ERROR: GrowlPathUtil was asked for directory 0x%x, but it doesn't know what directory that is. Please tell the Growl developers.", directory);
+				return nil;
+		}
+		if (subpath)
+			subpath = [@"Application Support/Growl" stringByAppendingPathComponent:subpath];
+		else
+			subpath =  @"Application Support/Growl";
+
+		/*get the search path, and append the subpath to all the items therein.
+		 *exclude results that don't exist.
+		 */
+		NSFileManager *mgr = [NSFileManager defaultManager];
+		BOOL isDir = NO;
+
+		NSArray *searchPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, domainMask, /*expandTilde*/ YES);
+		NSMutableArray *mSearchPath = [NSMutableArray arrayWithCapacity:[searchPath count]];
+		NSEnumerator *searchPathEnum = [searchPath objectEnumerator];
+		NSString *path;
+		while ((path = [searchPathEnum nextObject])) {
+			path = [path stringByAppendingPathComponent:subpath];
+			if ([mgr fileExistsAtPath:path isDirectory:&isDir] && isDir)
+				[mSearchPath addObject:path];
+		}
+
+		return mSearchPath;
+	}
+}
+
++ (NSArray *) searchPathForDirectory:(GrowlSearchPathDirectory) directory inDomains:(GrowlSearchPathDomainMask) domainMask {
+	//NO to emulate the default NSSearchPathForDirectoriesInDomains behaviour.
+	return [self searchPathForDirectory:directory inDomains:domainMask mustBeWritable:NO];
+}
+
++ (NSString *) growlSupportDirectory {
+	NSArray *searchPath = [self searchPathForDirectory:GrowlSupportDirectory inDomains:NSUserDomainMask mustBeWritable:YES];
+	if ([searchPath count])
+		return [searchPath objectAtIndex:0U];
+	else {
+		NSString *path = nil;
+
+		//if this doesn't return any writable directories, path will still be nil.
+		searchPath = [self searchPathForDirectory:NSLibraryDirectory inDomains:NSAllDomainsMask mustBeWritable:YES];
+		if ([searchPath count]) {
+			path = [[searchPath objectAtIndex:0U] stringByAppendingPathComponent:@"Application Support/Growl"];
+			//try to create it. if that doesn't work, don't return it. return nil instead.
+			if (![[NSFileManager defaultManager] createDirectoryAtPath:path attributes:nil])
+				path = nil;
+		}
+
+		return path;
+	}
+}
+
++ (NSString *) screenshotsDirectory {
+	NSArray *searchPath = [self searchPathForDirectory:GrowlScreenshotsDirectory inDomains:NSAllDomainsMask mustBeWritable:YES];
+	if ([searchPath count])
+		return [searchPath objectAtIndex:0U];
+	else {
+		NSString *path = nil;
+
+		//if this doesn't return any writable directories, path will still be nil.
+		path = [self growlSupportDirectory];
+		if (path) {
+			path = [path stringByAppendingPathComponent:NAME_OF_SCREENSHOTS_DIRECTORY];
+			//try to create it. if that doesn't work, don't return it. return nil instead.
+			if (![[NSFileManager defaultManager] createDirectoryAtPath:path attributes:nil])
+				path = nil;
+		}
+
+		return path;
+	}
+}
+
++ (NSString *) ticketsDirectory {
+	NSArray *searchPath = [self searchPathForDirectory:GrowlTicketsDirectory inDomains:NSAllDomainsMask mustBeWritable:YES];
+	if ([searchPath count])
+		return [searchPath objectAtIndex:0U];
+	else {
+		NSString *path = nil;
+
+		//if this doesn't return any writable directories, path will still be nil.
+		path = [self growlSupportDirectory];
+		if (path) {
+			path = [path stringByAppendingPathComponent:NAME_OF_TICKETS_DIRECTORY];
+			//try to create it. if that doesn't work, don't return it. return nil instead.
+			if (![[NSFileManager defaultManager] createDirectoryAtPath:path attributes:nil])
+				path = nil;
+		}
+
+		return path;
+	}
+}
+
+#pragma mark -
+#pragma mark Screenshot names
+
++ (NSString *) nextScreenshotName {
+	return [self nextScreenshotNameInDirectory:nil];
+}
+
++ (NSString *) nextScreenshotNameInDirectory:(NSString *) directory {
+	NSFileManager *mgr = [NSFileManager defaultManager];
+
+	if (!directory)
+		directory = [GrowlPathUtilities screenshotsDirectory];
+
+	//build a set of all the files in the directory, without their filename extensions.
+	NSArray *origContents = [mgr directoryContentsAtPath:directory];
+	NSMutableSet *directoryContents = [[NSMutableSet alloc] initWithCapacity:[origContents count]];
+
+	NSEnumerator *filesEnum = [origContents objectEnumerator];
+	NSString *existingFilename;
+	while ((existingFilename = [filesEnum nextObject]))
+		[directoryContents addObject:[existingFilename stringByDeletingPathExtension]];
+
+	//look for a filename that doesn't exist (with any extension) in the directory.
+	NSString *filename = nil;
+	unsigned long long i;
+	for (i = 1ULL; i < ULLONG_MAX; ++i) {
+		[filename release];
+		filename = [[NSString alloc] initWithFormat:@"Screenshot %llu", i];
+		if (![directoryContents containsObject:filename])
+			break;
+	}
+	[directoryContents release];
+
+	return [filename autorelease];
+}
+
+#pragma mark -
+#pragma mark Tickets
+
++ (NSString *) defaultSavePathForTicketWithApplicationName:(NSString *) appName {
+	return [[self ticketsDirectory] stringByAppendingPathComponent:[appName stringByAppendingPathExtension:GROWL_PATHEXTENSION_TICKET]];
+}
+
+@end
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/GrowlPathway.h
@@ -0,0 +1,23 @@
+//
+//  GrowlNotificationServer.h
+//  Growl
+//
+//  Created by Ingmar Stein on 15.11.04.
+//  Copyright 2004-2006 The Growl Project. All rights reserved.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+#import <Foundation/Foundation.h>
+
+@protocol GrowlNotificationProtocol
+- (oneway void) registerApplicationWithDictionary:(bycopy NSDictionary *)dict;
+- (oneway void) postNotificationWithDictionary:(bycopy NSDictionary *)notification;
+- (bycopy NSString *) growlVersion;
+@end
+
+@class GrowlApplicationController;
+
+@interface GrowlPathway : NSObject <GrowlNotificationProtocol> {
+}
+
+@end
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/GrowlPreferencesController.h
@@ -0,0 +1,128 @@
+//
+//  GrowlPreferencesController.h
+//  Growl
+//
+//  Created by Nelson Elhage on 8/24/04.
+//  Renamed from GrowlPreferences.h by Mac-arena the Bored Zo on 2005-06-27.
+//  Copyright 2004-2006 The Growl Project. All rights reserved.
+//
+// This file is under the BSD License, refer to License.txt for details
+
+#ifndef GROWL_PREFERENCES_CONTROLLER_H
+#define GROWL_PREFERENCES_CONTROLLER_H
+
+#ifdef __OBJC__
+#define XSTR(x) (@x)
+#else
+#define XSTR CFSTR
+#endif
+
+#define GrowlPreferencesChanged		XSTR("GrowlPreferencesChanged")
+#define GrowlPreview				XSTR("GrowlPreview")
+#define GrowlDisplayPluginKey		XSTR("GrowlDisplayPluginName")
+#define GrowlUserDefaultsKey		XSTR("GrowlUserDefaults")
+#define GrowlStartServerKey			XSTR("GrowlStartServer")
+#define GrowlRemoteRegistrationKey	XSTR("GrowlRemoteRegistration")
+#define GrowlEnableForwardKey		XSTR("GrowlEnableForward")
+#define GrowlForwardDestinationsKey	XSTR("GrowlForwardDestinations")
+#define GrowlUDPPortKey				XSTR("GrowlUDPPort")
+#define GrowlTCPPortKey				XSTR("GrowlTCPPort")
+#define GrowlUpdateCheckKey			XSTR("GrowlUpdateCheck")
+#define LastUpdateCheckKey			XSTR("LastUpdateCheck")
+#define	GrowlLoggingEnabledKey		XSTR("GrowlLoggingEnabled")
+#define	GrowlLogTypeKey				XSTR("GrowlLogType")
+#define	GrowlCustomHistKey1			XSTR("Custom log history 1")
+#define	GrowlCustomHistKey2			XSTR("Custom log history 2")
+#define	GrowlCustomHistKey3			XSTR("Custom log history 3")
+#define GrowlMenuExtraKey			XSTR("GrowlMenuExtra")
+#define GrowlSquelchModeKey			XSTR("GrowlSquelchMode")
+#define LastKnownVersionKey			XSTR("LastKnownVersion")
+#define GrowlStickyWhenAwayKey		XSTR("StickyWhenAway")
+#define GrowlStickyIdleThresholdKey	XSTR("IdleThreshold")
+
+CFTypeRef GrowlPreferencesController_objectForKey(CFTypeRef key);
+CFIndex   GrowlPreferencesController_integerForKey(CFTypeRef key);
+Boolean   GrowlPreferencesController_boolForKey(CFTypeRef key);
+unsigned short GrowlPreferencesController_unsignedShortForKey(CFTypeRef key);
+
+#ifdef __OBJC__
+
+#import "GrowlAbstractSingletonObject.h"
+
+@interface GrowlPreferencesController : GrowlAbstractSingletonObject {
+	void *loginItems;
+}
+
++ (GrowlPreferencesController *) sharedController;
+
+- (void) registerDefaults:(NSDictionary *)inDefaults;
+- (id) objectForKey:(NSString *)key;
+- (void) setObject:(id)object forKey:(NSString *)key;
+- (BOOL) boolForKey:(NSString*) key;
+- (void) setBool:(BOOL)value forKey:(NSString *)key;
+- (CFIndex) integerForKey:(NSString *)key;
+- (void) setInteger:(CFIndex)value forKey:(NSString *)key;
+- (void) synchronize;
+
+- (BOOL) shouldStartGrowlAtLogin;
+- (void) setShouldStartGrowlAtLogin:(BOOL)flag;
+- (void) setStartAtLogin:(NSString *)path enabled:(BOOL)flag;
+
+- (BOOL) isRunning:(NSString *)theBundleIdentifier;
+- (BOOL) isGrowlRunning;
+- (void) setGrowlRunning:(BOOL)flag noMatterWhat:(BOOL)nmw;
+- (void) launchGrowl:(BOOL)noMatterWhat;
+- (void) terminateGrowl;
+
+#pragma mark GrowlMenu running state
+
+- (void) enableGrowlMenu;
+- (void) disableGrowlMenu;
+
+#pragma mark -
+//Simplified accessors
+
+#pragma mark UI
+
+- (BOOL) isBackgroundUpdateCheckEnabled;
+- (void) setIsBackgroundUpdateCheckEnabled:(BOOL)flag;
+
+- (NSString *) defaultDisplayPluginName;
+- (void) setDefaultDisplayPluginName:(NSString *)name;
+
+- (BOOL) squelchMode;
+- (void) setSquelchMode:(BOOL)flag;
+
+- (BOOL) stickyWhenAway;
+- (void) setStickyWhenAway:(BOOL)flag;
+
+- (NSNumber*) idleThreshold;
+- (void) setIdleThreshold:(NSNumber*)value;
+
+#pragma mark GrowlMenu methods
+
+- (BOOL) isGrowlMenuEnabled;
+- (void) setGrowlMenuEnabled:(BOOL)state;
+
+#pragma mark "Network" tab pane
+
+- (BOOL) isGrowlServerEnabled;
+- (void) setGrowlServerEnabled:(BOOL)enabled;
+
+- (BOOL) isRemoteRegistrationAllowed;
+- (void) setRemoteRegistrationAllowed:(BOOL)flag;
+
+- (BOOL) isForwardingEnabled;
+- (void) setForwardingEnabled:(BOOL)enabled;
+
+- (NSString *) remotePassword;
+- (void) setRemotePassword:(NSString *)value;
+
+- (unsigned short) UDPPort;
+- (void) setUDPPort:(unsigned short)value;
+
+@end
+
+#endif
+
+#endif
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/GrowlTicketController.h
@@ -0,0 +1,29 @@
+//
+//  GrowlTicketController.h
+//  Growl
+//
+//  Created by Mac-arena the Bored Zo on 2005-06-08.
+//  Copyright 2005-2006 Mac-arena the Bored Zo. All rights reserved.
+//
+
+#import "GrowlAbstractSingletonObject.h"
+
+#define GROWL_PATHEXTENSION_TICKET	@"growlTicket"
+
+@class GrowlApplicationTicket;
+
+@interface GrowlTicketController: GrowlAbstractSingletonObject
+{
+	NSMutableDictionary *ticketsByApplicationName;
+}
+
++ (id) sharedController;
+
+- (NSDictionary *) allSavedTickets;
+
+- (GrowlApplicationTicket *) ticketForApplicationName:(NSString *) appName;
+- (void) addTicket:(GrowlApplicationTicket *) newTicket;
+- (void) removeTicketForApplicationName:(NSString *)appName;
+
+- (void) loadAllSavedTickets;
+@end
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/Makefile.in
@@ -0,0 +1,27 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = alerts
+LIBRARY_NAME = growl_s
+FORCE_STATIC_LIB = 1
+
+CMSRCS = \
+  GrowlApplicationBridge.m \
+  GrowlPathUtilities.m \
+  $(NULL)
+
+CSRCS = \
+  CFGrowlAdditions.c \
+  CFMutableDictionaryAdditions.c \
+  CFURLAdditions.c \
+	$(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/growl/license.txt
@@ -0,0 +1,15 @@
+Copyright (c) The Growl Project, 2004-2011
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of Growl nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/mozGrowlDelegate.h
@@ -0,0 +1,106 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#import "GrowlApplicationBridge.h"
+
+#include "nscore.h"
+#include "nsStringGlue.h"
+
+#import <Cocoa/Cocoa.h>
+
+#define GROWL_STRING_BUNDLE_LOCATION \
+  "chrome://alerts/locale/notificationNames.properties"
+
+#define OBSERVER_KEY      @"ALERT_OBSERVER"
+#define COOKIE_KEY        @"ALERT_COOKIE"
+
+class nsIObserver;
+class nsIDOMWindow;
+
+@interface mozGrowlDelegate : NSObject <GrowlApplicationBridgeDelegate>
+{
+@private
+  uint32_t mKey;
+  NSMutableDictionary *mDict;
+  NSMutableArray *mNames;
+  NSMutableArray *mEnabled;
+}
+
+/**
+ * Dispatches a notification to Growl
+ *
+ * @param aName   The notification name to dispatch
+ * @param aTitle  The title of the notification
+ * @param aText   The body of the notification
+ * @param aImage  The image data, or [NSData data] if no image
+ * @param aKey    The observer key to use as a lookup (or 0 if no observer)
+ * @param aCookie The string to be used as a cookie if there is an observer
+ */
++ (void) notifyWithName:(const nsAString&)aName
+                  title:(const nsAString&)aTitle
+            description:(const nsAString&)aText
+               iconData:(NSData*)aImage
+                    key:(uint32_t)aKey
+                 cookie:(const nsAString&)aCookie;
+
+/**
+ * Adds notifications to the registration dictionary.
+ *
+ * @param aNames An NSArray of names of notifications.
+ */
+- (void) addNotificationNames:(NSArray*)aNames;
+
+/**
+ * Adds enabled notifications to the registration dictionary.
+ *
+ * @param aEnabled An NSArray of names of enabled notifications.
+ */
+- (void) addEnabledNotifications:(NSArray*)aEnabled;
+
+/**
+ * Adds an nsIObserver that we can query later for dispatching obsevers.
+ *
+ * @param aObserver The observer we are adding.
+ * @return The key it was stored in.
+ */
+- (uint32_t) addObserver:(nsIObserver*)aObserver;
+
+/**
+ * Gives Growl the application name.
+ */
+- (NSString*) applicationNameForGrowl;
+
+/**
+ * Gives Growl the complete list of notifications this application will ever
+ * send, and also which notifications should be enabled by default.
+ */
+- (NSDictionary *) registrationDictionaryForGrowl;
+
+/**
+ * Informs us that a Growl notification timed out.
+ *
+ * @param clickContext The object passed back from growl.
+ */
+- (void) growlNotificationTimedOut:(id)clickContext;
+
+/**
+ * Informs us that a Growl notification was clicked.
+ *
+ * @param clickContext The object passed back from growl.
+ */
+- (void) growlNotificationWasClicked:(id)clickContext;
+
+/**
+ * Called when a window was closed and the observers are no longer valid.
+ *
+ * @param window The window that was closed.
+ */
+- (void) forgetObserversForWindow:(nsIDOMWindow*)window;
+
+/**
+ * Called when all observers should be released.
+ */
+- (void) forgetObservers;
+
+@end
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/mozGrowlDelegate.mm
@@ -0,0 +1,268 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#import "mozGrowlDelegate.h"
+#import "ObserverPair.h"
+
+#include "nsIObserver.h"
+#include "nsIXULAppInfo.h"
+#include "nsIStringBundle.h"
+#include "nsIDOMWindow.h"
+
+#include "nsObjCExceptions.h"
+
+@implementation mozGrowlDelegate
+
+- (id) init
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+  if ((self = [super init])) {
+    mKey = 0;
+    mDict = [[NSMutableDictionary dictionaryWithCapacity: 8] retain];
+    
+    mNames   = [[NSMutableArray alloc] init];
+    mEnabled = [[NSMutableArray alloc] init];
+  
+    nsresult rv;
+    nsCOMPtr<nsIStringBundleService> bundleService =
+      do_GetService("@mozilla.org/intl/stringbundle;1", &rv);
+
+    if (NS_SUCCEEDED(rv)) {
+      nsCOMPtr<nsIStringBundle> bundle;
+      rv = bundleService->CreateBundle(GROWL_STRING_BUNDLE_LOCATION,
+                                       getter_AddRefs(bundle));
+      
+      if (NS_SUCCEEDED(rv)) {
+        nsString text;
+        rv = bundle->GetStringFromName(NS_LITERAL_STRING("general").get(),
+                                       getter_Copies(text));
+        
+        if (NS_SUCCEEDED(rv)) {
+          NSString *s = [NSString stringWithCharacters: text.BeginReading()
+                                                length: text.Length()];
+          [mNames addObject: s];
+          [mEnabled addObject: s];
+
+          return self;
+        }
+      }
+    }
+
+    // Fallback
+    [mNames addObject: @"General Notification"];
+    [mEnabled addObject: @"General Notification"];
+  }
+
+  return self;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (void) dealloc
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  [mDict release];
+
+  [mNames release];
+  [mEnabled release];
+
+  [super dealloc];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void) addNotificationNames:(NSArray*)aNames
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  [mNames addObjectsFromArray: aNames];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void) addEnabledNotifications:(NSArray*)aEnabled
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  [mEnabled addObjectsFromArray: aEnabled];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
++ (void) notifyWithName:(const nsAString&)aName
+                  title:(const nsAString&)aTitle
+            description:(const nsAString&)aText
+               iconData:(NSData*)aImage
+                    key:(uint32_t)aKey
+                 cookie:(const nsAString&)aCookie
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  NS_ASSERTION(aName.Length(), "No name specified for the alert!");
+  
+  NSDictionary* clickContext = nil;
+  if (aKey) {
+    clickContext = [NSDictionary dictionaryWithObjectsAndKeys:
+      [NSNumber numberWithUnsignedInt: aKey],
+      OBSERVER_KEY,
+      [NSArray arrayWithObject:
+        [NSString stringWithCharacters: aCookie.BeginReading()
+                                length: aCookie.Length()]],
+      COOKIE_KEY,
+      nil];
+  }
+
+  [GrowlApplicationBridge
+     notifyWithTitle: [NSString stringWithCharacters: aTitle.BeginReading()
+                                              length: aTitle.Length()]
+         description: [NSString stringWithCharacters: aText.BeginReading()
+                                              length: aText.Length()]
+    notificationName: [NSString stringWithCharacters: aName.BeginReading()
+                                              length: aName.Length()]
+            iconData: aImage
+            priority: 0
+            isSticky: NO
+        clickContext: clickContext];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (uint32_t) addObserver:(nsIObserver *)aObserver
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+  nsCOMPtr<nsIDOMWindow> parentWindow = GetWindowOfObserver(aObserver);
+
+  ObserverPair* pair = [[ObserverPair alloc] initWithObserver: aObserver
+                                                       window: parentWindow];
+  [pair autorelease];
+
+  [mDict setObject: pair
+            forKey: [NSNumber numberWithUnsignedInt: ++mKey]];
+  return mKey;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
+}
+
+- (NSDictionary *) registrationDictionaryForGrowl
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+  return [NSDictionary dictionaryWithObjectsAndKeys:
+           mNames, GROWL_NOTIFICATIONS_ALL,
+           mEnabled, GROWL_NOTIFICATIONS_DEFAULT,
+           nil];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (NSString*) applicationNameForGrowl
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+  nsresult rv;
+
+  nsCOMPtr<nsIXULAppInfo> appInfo =
+    do_GetService("@mozilla.org/xre/app-info;1", &rv);
+  NS_ENSURE_SUCCESS(rv, nil);
+
+  nsAutoCString appName;
+  rv = appInfo->GetName(appName);
+  NS_ENSURE_SUCCESS(rv, nil);
+
+  nsAutoString name = NS_ConvertUTF8toUTF16(appName);
+  return [NSString stringWithCharacters: name.BeginReading()
+                                 length: name.Length()];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (void) growlNotificationTimedOut:(id)clickContext
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  NS_ASSERTION([clickContext valueForKey: OBSERVER_KEY] != nil,
+               "OBSERVER_KEY not found!");
+  NS_ASSERTION([clickContext valueForKey: COOKIE_KEY] != nil,
+               "COOKIE_KEY not found!");
+
+  ObserverPair* pair =
+    [mDict objectForKey: [clickContext valueForKey: OBSERVER_KEY]];
+  nsCOMPtr<nsIObserver> observer = pair ? pair->observer : nullptr;
+
+  [mDict removeObjectForKey: [clickContext valueForKey: OBSERVER_KEY]];
+  NSString* cookie = [[clickContext valueForKey: COOKIE_KEY] objectAtIndex: 0];
+
+  if (observer) {
+    nsAutoString tmp;
+    tmp.SetLength([cookie length]);
+    [cookie getCharacters:tmp.BeginWriting()];
+
+    observer->Observe(nullptr, "alertfinished", tmp.get());
+  }
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void) growlNotificationWasClicked:(id)clickContext
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  NS_ASSERTION([clickContext valueForKey: OBSERVER_KEY] != nil,
+               "OBSERVER_KEY not found!");
+  NS_ASSERTION([clickContext valueForKey: COOKIE_KEY] != nil,
+               "COOKIE_KEY not found!");
+
+  ObserverPair* pair =
+    [mDict objectForKey: [clickContext valueForKey: OBSERVER_KEY]];
+  nsCOMPtr<nsIObserver> observer = pair ? pair->observer : nullptr;
+
+  [mDict removeObjectForKey: [clickContext valueForKey: OBSERVER_KEY]];
+  NSString* cookie = [[clickContext valueForKey: COOKIE_KEY] objectAtIndex: 0];
+
+  if (observer) {
+    nsAutoString tmp;
+    tmp.SetLength([cookie length]);
+    [cookie getCharacters:tmp.BeginWriting()];
+
+    observer->Observe(nullptr, "alertclickcallback", tmp.get());
+    observer->Observe(nullptr, "alertfinished", tmp.get());
+  }
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void) forgetObserversForWindow:(nsIDOMWindow*)window
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  NS_ASSERTION(window, "No window!");
+
+  NSMutableArray *keysToRemove = [NSMutableArray array];
+
+  NSEnumerator *keyEnumerator = [[mDict allKeys] objectEnumerator];
+  NSNumber *key;
+  while ((key = [keyEnumerator nextObject])) {
+    ObserverPair *pair = [mDict objectForKey:key];
+    if (pair && pair->window == window)
+      [keysToRemove addObject:key];
+  }
+
+  [mDict removeObjectsForKeys:keysToRemove];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void) forgetObservers
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  [mDict removeAllObjects];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+@end
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/nsAlertsImageLoadListener.h
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAlertsImageLoadListener_h_
+#define nsAlertsImageLoadListener_h_
+
+#import "mozGrowlDelegate.h"
+
+#include "nsIStreamLoader.h"
+#include "nsStringAPI.h"
+#include "mozilla/Attributes.h"
+
+class nsAlertsImageLoadListener MOZ_FINAL : public nsIStreamLoaderObserver
+{
+public:
+  nsAlertsImageLoadListener(const nsAString &aName,
+                            const nsAString& aAlertTitle,
+                            const nsAString& aAlertText,
+                            const nsAString& aAlertCookie,
+                            uint32_t aAlertListenerKey);
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISTREAMLOADEROBSERVER
+private:
+  nsString mName;
+  nsString mAlertTitle;
+  nsString mAlertText;
+  nsString mAlertCookie;
+  uint32_t mAlertListenerKey;
+};
+
+#endif // nsAlertsImageLoadListener_h_
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/nsAlertsImageLoadListener.mm
@@ -0,0 +1,67 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsAlertsImageLoadListener.h"
+#include "nsObjCExceptions.h"
+
+#ifdef DEBUG
+#include "nsIRequest.h"
+#include "nsIChannel.h"
+#include "nsIURI.h"
+#include "nsCOMPtr.h"
+#endif
+
+NS_IMPL_ISUPPORTS1(nsAlertsImageLoadListener, nsIStreamLoaderObserver)
+
+nsAlertsImageLoadListener::nsAlertsImageLoadListener(const nsAString &aName,
+                                                     const nsAString& aAlertTitle,
+                                                     const nsAString& aAlertText,
+                                                     const nsAString& aAlertCookie,
+                                                     uint32_t aAlertListenerKey) :
+  mName(aName), mAlertTitle(aAlertTitle), mAlertText(aAlertText),
+  mAlertCookie(aAlertCookie), mAlertListenerKey(aAlertListenerKey)
+{
+}
+
+NS_IMETHODIMP
+nsAlertsImageLoadListener::OnStreamComplete(nsIStreamLoader* aLoader,
+                                            nsISupports* aContext,
+                                            nsresult aStatus,
+                                            uint32_t aLength,
+                                            const uint8_t* aResult)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+#ifdef DEBUG
+  // print a load error on bad status
+  nsCOMPtr<nsIRequest> request;
+  aLoader->GetRequest(getter_AddRefs(request));
+  nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
+
+  if (NS_FAILED(aStatus)) {
+    if (channel) {
+      nsCOMPtr<nsIURI> uri;
+      channel->GetURI(getter_AddRefs(uri));
+      if (uri) {
+        nsAutoCString uriSpec;
+        uri->GetSpec(uriSpec);
+        printf("Failed to load %s\n", uriSpec.get());
+      }
+    }
+  }
+#endif
+
+  [mozGrowlDelegate notifyWithName: mName
+                             title: mAlertTitle
+                       description: mAlertText
+                          iconData: NS_FAILED(aStatus) ? [NSData data] :
+                                      [NSData dataWithBytes: aResult
+                                                     length: aLength]
+                               key: mAlertListenerKey
+                            cookie: mAlertCookie];
+
+  return NS_OK;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
--- a/toolkit/components/alerts/mac/nsMacAlertsService.h
+++ b/toolkit/components/alerts/mac/nsMacAlertsService.h
@@ -3,29 +3,32 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsMacAlertsService_h_
 #define nsMacAlertsService_h_
 
 #include "nsIAlertsService.h"
 #include "nsIObserver.h"
 
+struct GrowlDelegateWrapper;
 struct NotificationCenterDelegateWrapper;
 
 class nsMacAlertsService : public nsIAlertsService,
                            public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIALERTSSERVICE
   NS_DECL_NSIOBSERVER
 
   nsMacAlertsService();
   nsresult Init();
 
 private:
   virtual ~nsMacAlertsService();
+  nsresult InitGrowl();
   nsresult InitNotificationCenter();
 
+  GrowlDelegateWrapper* mGrowlDelegate;
   NotificationCenterDelegateWrapper* mNCDelegate;
 };
 
 #endif // nsMacAlertsService_h_
--- a/toolkit/components/alerts/mac/nsMacAlertsService.mm
+++ b/toolkit/components/alerts/mac/nsMacAlertsService.mm
@@ -3,29 +3,113 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #import <CoreServices/CoreServices.h>
 
 #include "nsMacAlertsService.h"
 #include "nsStringAPI.h"
+#include "nsAlertsImageLoadListener.h"
 #include "nsIURI.h"
 #include "nsIStreamLoader.h"
 #include "nsNetUtil.h"
 #include "nsCOMPtr.h"
+#include "nsIStringBundle.h"
 #include "nsIObserverService.h"
 #include "nsAutoPtr.h"
+#include "nsNotificationsList.h"
 #include "nsObjCExceptions.h"
 #include "nsPIDOMWindow.h"
 
+#import "mozGrowlDelegate.h"
 #import "mozNotificationCenterDelegate.h"
+#import "GrowlApplicationBridge.h"
 #import "nsNotificationCenterCompat.h"
 
 ////////////////////////////////////////////////////////////////////////////////
+//// GrowlDelegateWrapper
+
+struct GrowlDelegateWrapper
+{
+  mozGrowlDelegate* mDelegate;
+  
+  GrowlDelegateWrapper()
+  {
+    NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+    
+    mDelegate = [[mozGrowlDelegate alloc] init];
+    
+    NS_OBJC_END_TRY_ABORT_BLOCK;
+  }
+  
+  ~GrowlDelegateWrapper()
+  {
+    NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+    
+    [mDelegate release];
+    
+    NS_OBJC_END_TRY_ABORT_BLOCK;
+  }
+};
+
+/**
+ * Helper function to dispatch a notification to Growl
+ */
+static nsresult
+DispatchGrowlNotification(const nsAString &aName,
+                          const nsAString &aImage,
+                          const nsAString &aTitle,
+                          const nsAString &aMessage,
+                          const nsAString &aCookie,
+                          nsIObserver *aListener)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+  
+  if ([GrowlApplicationBridge isGrowlRunning] == NO) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  
+  mozGrowlDelegate *delegate =
+  static_cast<mozGrowlDelegate *>([GrowlApplicationBridge growlDelegate]);
+  if (!delegate) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  
+  uint32_t ind = 0;
+  if (aListener) {
+    ind = [delegate addObserver: aListener];
+  }
+  
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = NS_NewURI(getter_AddRefs(uri), aImage);
+  if (NS_FAILED(rv)) {
+    // image uri failed to resolve, so dispatch to growl with no image
+    [mozGrowlDelegate notifyWithName: aName
+                               title: aTitle
+                         description: aMessage
+                            iconData: [NSData data]
+                                 key: ind
+                              cookie: aCookie];
+    return NS_OK;
+  }
+  
+  nsCOMPtr<nsAlertsImageLoadListener> listener =
+  new nsAlertsImageLoadListener(aName, aTitle, aMessage, aCookie, ind);
+  if (!listener) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  
+  nsCOMPtr<nsIStreamLoader> loader;
+  return NS_NewStreamLoader(getter_AddRefs(loader), uri, listener);
+  
+  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+////////////////////////////////////////////////////////////////////////////////
 //// NotificationCenterDelegateWrapper
 
 struct NotificationCenterDelegateWrapper
 {
   mozNotificationCenterDelegate* mDelegate;
 
   NotificationCenterDelegateWrapper()
   {
@@ -95,98 +179,182 @@ NS_INTERFACE_MAP_BEGIN(nsMacAlertsServic
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAlertsService)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsIAlertsService)
 NS_INTERFACE_MAP_END_THREADSAFE
 
 nsresult
 nsMacAlertsService::Init()
 {
-  // Notification Center support isn't ready, don't use it.
-  return NS_ERROR_NOT_AVAILABLE;
-/*
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
+
   nsresult rv;
   nsCOMPtr<nsIObserverService> os =
     do_GetService("@mozilla.org/observer-service;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
+// Notification Center support isn't ready, don't use it.
+/*
   // Only use Notification Center with OS version >= 10.8.
   SInt32 osVersion = 0;
   OSErr err = ::Gestalt (gestaltSystemVersion, &osVersion);
   osVersion &= 0xFFFF; // The system version is in the low order word
   if (err != noErr || (osVersion < 0x00001080)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   rv = InitNotificationCenter();
   if (NS_FAILED(rv)) {
     return rv;
   }
+*/
+
+  InitGrowl();
 
   (void)os->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, false);
   (void)os->AddObserver(this, "profile-before-change", false);
 
   return rv;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
-*/
+}
+
+nsresult
+nsMacAlertsService::InitGrowl() {
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+  NS_ASSERTION([GrowlApplicationBridge growlDelegate] == nil,
+               "We already registered with Growl!");
+
+  nsresult rv;
+  nsCOMPtr<nsIObserverService> os =
+  do_GetService("@mozilla.org/observer-service;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<nsNotificationsList> notifications = new nsNotificationsList();
+
+  if (notifications) {
+    (void)os->NotifyObservers(notifications, "before-growl-registration", nullptr);
+  }
+
+  mGrowlDelegate = new GrowlDelegateWrapper();
+
+  if (notifications) {
+    notifications->informController(mGrowlDelegate->mDelegate);
+  }
+
+  [GrowlApplicationBridge setGrowlDelegate: mGrowlDelegate->mDelegate];
+
+  return NS_OK;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 nsresult
 nsMacAlertsService::InitNotificationCenter() {
   mNCDelegate = new NotificationCenterDelegateWrapper();
   return NS_OK;
 }
 
 nsMacAlertsService::nsMacAlertsService()
-: mNCDelegate(nullptr)
+: mGrowlDelegate(nullptr)
+, mNCDelegate(nullptr)
 {}
 
 nsMacAlertsService::~nsMacAlertsService()
 {
+  delete mGrowlDelegate;
   delete mNCDelegate;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIAlertsService
 
 NS_IMETHODIMP
 nsMacAlertsService::ShowAlertNotification(const nsAString& aImageUrl,
                                             const nsAString& aAlertTitle,
                                             const nsAString& aAlertText,
                                             bool aAlertClickable,
                                             const nsAString& aAlertCookie,
                                             nsIObserver* aAlertListener,
                                             const nsAString& aAlertName)
 {
-  return DispatchNCNotification(mNCDelegate, aAlertTitle, aAlertText,
-                                aAlertCookie, aAlertListener);
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+  // Growl requires a few extra steps, like checking that it's registered and
+  // the alert has a name.
+  if (mGrowlDelegate) {
+    NS_ASSERTION(mGrowlDelegate->mDelegate == [GrowlApplicationBridge growlDelegate],
+                 "Growl Delegate was not registered properly.");
+
+    if (!aAlertName.IsEmpty()) {
+      return DispatchGrowlNotification(aAlertName, aImageUrl, aAlertTitle,
+                                       aAlertText, aAlertCookie, aAlertListener);
+    }
+
+    nsresult rv;
+    nsCOMPtr<nsIStringBundleService> bundleService =
+    do_GetService("@mozilla.org/intl/stringbundle;1", &rv);
+
+    // We don't want to fail just yet if we can't get the alert name
+    nsString name = NS_LITERAL_STRING("General Notification");
+    if (NS_SUCCEEDED(rv)) {
+      nsCOMPtr<nsIStringBundle> bundle;
+      rv = bundleService->CreateBundle(GROWL_STRING_BUNDLE_LOCATION,
+                                       getter_AddRefs(bundle));
+      if (NS_SUCCEEDED(rv)) {
+        rv = bundle->GetStringFromName(NS_LITERAL_STRING("general").get(),
+                                       getter_Copies(name));
+        if (NS_FAILED(rv)) {
+          name = NS_LITERAL_STRING("General Notification");
+        }
+      }
+    }
+    
+    return DispatchGrowlNotification(name, aImageUrl, aAlertTitle,
+                                     aAlertText, aAlertCookie, aAlertListener);
+  } else {
+    // Notification Center is easy: no image, no name.
+    return DispatchNCNotification(mNCDelegate, aAlertTitle, aAlertText,
+                                  aAlertCookie, aAlertListener);
+  }
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIObserver
 
 NS_IMETHODIMP
 nsMacAlertsService::Observe(nsISupports* aSubject, const char* aTopic,
                               const PRUnichar* aData)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
-  if (!mNCDelegate) {
+  if (!mGrowlDelegate && !mNCDelegate) {
     return NS_OK;
   }
 
   if (strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0) {
     nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(aSubject));
     if (window) {
-      [mNCDelegate->mDelegate forgetObserversForWindow:window];
+      if (mGrowlDelegate) {
+        [mGrowlDelegate->mDelegate forgetObserversForWindow:window];
+      }
+      else if (mNCDelegate) {
+        [mNCDelegate->mDelegate forgetObserversForWindow:window];
+      }
     }
   }
   else if (strcmp(aTopic, "profile-before-change") == 0) {
-    [mNCDelegate->mDelegate forgetObservers];
+    if (mGrowlDelegate) {
+      [mGrowlDelegate->mDelegate forgetObservers];
+    }
+    else if (mNCDelegate) {
+      [mNCDelegate->mDelegate forgetObservers];
+    }
   }
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/nsNotificationsList.h
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsNotificationsList_h_
+#define nsNotificationsList_h_
+
+#include "nsINotificationsList.h"
+#import "mozGrowlDelegate.h"
+
+class nsNotificationsList : public nsINotificationsList
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSINOTIFICATIONSLIST
+
+  nsNotificationsList();
+
+  void informController(mozGrowlDelegate *aCont);
+private:
+  virtual ~nsNotificationsList();
+
+  NSMutableArray *mNames;
+  NSMutableArray *mEnabled;
+};
+
+#endif // nsNotificationsList_h_
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/mac/nsNotificationsList.mm
@@ -0,0 +1,79 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsNotificationsList.h"
+#include "nsObjCExceptions.h"
+#include "nsStringAPI.h"
+
+NS_IMPL_ADDREF(nsNotificationsList)
+NS_IMPL_RELEASE(nsNotificationsList)
+
+NS_INTERFACE_MAP_BEGIN(nsNotificationsList)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINotificationsList)
+  NS_INTERFACE_MAP_ENTRY(nsINotificationsList)
+NS_INTERFACE_MAP_END
+
+nsNotificationsList::nsNotificationsList()
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  mNames   = [[NSMutableArray alloc] init];
+  mEnabled = [[NSMutableArray alloc] init];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+nsNotificationsList::~nsNotificationsList()
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  [mNames release];
+  [mEnabled release];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+NS_IMETHODIMP
+nsNotificationsList::AddNotification(const nsAString &aName, bool aEnabled)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+  NSString *name = [NSString stringWithCharacters: aName.BeginReading()
+                                           length: aName.Length()];
+
+  [mNames addObject: name];
+
+  if (aEnabled)
+    [mEnabled addObject: name];
+
+  return NS_OK;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsNotificationsList::IsNotification(const nsAString &aName, bool *retVal)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+  NSString *name = [NSString stringWithCharacters: aName.BeginReading()
+                                           length: aName.Length()];
+
+  *retVal = [mNames containsObject: name] ? true : false;
+  return NS_OK;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+void
+nsNotificationsList::informController(mozGrowlDelegate *aController)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  [aController addNotificationNames: mNames];
+  [aController addEnabledNotifications: mEnabled];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/alerts/nsINotificationsList.idl
@@ -0,0 +1,26 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(11e0176a-4a2d-4a90-8dcc-1590eeb1769d)]
+interface nsINotificationsList : nsISupports
+{
+    /**
+     * Adds a notification to the registration dictionary.
+     *
+     * @param aName The name of the notification we might be sending.
+     * @param aEnabled Indicates if the notification is enabled by default.
+     */
+    void addNotification(in AString aName, in boolean aEnabled);
+
+    /**
+     * Checks to see if a paticular notification has already been registered.
+     *
+     * @param aName The name of the notification we are checking against.
+     */
+    boolean isNotification(in AString aName);
+};
+
--- a/toolkit/components/alerts/test/Makefile.in
+++ b/toolkit/components/alerts/test/Makefile.in
@@ -9,16 +9,14 @@ srcdir    = @srcdir@
 VPATH   = @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 # Mochitest tests
 # Synchronous tests like test_alerts.html must come before
 # asynchronous tests like test_alerts_noobserve.html!
-ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 MOCHITEST_FILES = \
 		test_alerts.html \
 		test_alerts_noobserve.html \
 		$(NULL)
-endif
 
 include $(topsrcdir)/config/rules.mk
--- a/toolkit/content/license.html
+++ b/toolkit/content/license.html
@@ -70,16 +70,17 @@
       <li><a href="about:license#edl">Eclipse Distribution License</a></li>
       <li><a href="about:license#expat">Expat License</a></li>
       <li><a href="about:license#firebug">Firebug License</a></li>
       <li><a href="about:license#gfx-font-list">gfxFontList License</a></li>
       <li><a href="about:license#google-bsd">Google BSD License</a></li>
       <li><a href="about:license#gears">Google Gears License</a></li>
       <li><a href="about:license#gears-istumbler">Google Gears/iStumbler License</a></li>
       <li><a href="about:license#vp8">Google VP8 License</a></li>
+      <li><a href="about:license#growl">Growl License</a></li>
       <li><a href="about:license#gyp">gyp License</a></li>
       <li><a href="about:license#halloc">halloc License</a></li>
       <li><a href="about:license#harfbuzz">HarfBuzz License</a></li>
       <li><a href="about:license#icu">ICU License</a></li>
       <li><a href="about:license#jpnic">Japan Network Information Center License</a></li>
       <li><a href="about:license#jemalloc">jemalloc License</a></li>
       <li><a href="about:license#jquery">jQuery License</a></li>
       <li><a href="about:license#libcubeb">libcubeb License</a></li>
@@ -1526,16 +1527,55 @@ PROFITS; OR BUSINESS INTERRUPTION) HOWEV
 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 </pre>
 
 
 
     <hr>
+    <h1><a id="growl"></a>Growl License</h1>
+
+    <p>This license applies to certain files in the directory
+    <span class="path">toolkit/components/alerts/mac/growl/</span>.
+    (This code only ships in the Mac OS X version of the product.)
+
+<pre>
+Copyright (c) The Growl Project, 2004-2011
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of Growl nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+</pre>
+
+
+    <hr>
+
 
     <h1><a id="gyp"></a>gyp License</h1>
 
     <p>This license applies to certain files in the directory
     <span class="path">media/webrtc/trunk/tools/gyp</span>.</p>
 <pre>
 Copyright (c) 2009 Google Inc. All rights reserved.
 
--- a/toolkit/toolkit-makefiles.sh
+++ b/toolkit/toolkit-makefiles.sh
@@ -616,16 +616,17 @@ elif [ "$MOZ_WIDGET_TOOLKIT" = "cocoa" ]
     image/decoders/icon/mac/Makefile
     intl/locale/src/mac/Makefile
     netwerk/system/mac/Makefile
     toolkit/system/osxproxy/Makefile
     toolkit/themes/pinstripe/Makefile
     toolkit/themes/pinstripe/global/Makefile
     toolkit/themes/pinstripe/mozapps/Makefile
     toolkit/components/alerts/mac/Makefile
+    toolkit/components/alerts/mac/growl/Makefile
     widget/cocoa/Makefile
   "
 elif [ "$MOZ_WIDGET_TOOLKIT" = "gtk2" ]; then
   add_makefiles "
     image/decoders/icon/gtk/Makefile
     widget/gtk2/Makefile
   "
 elif [ "$MOZ_WIDGET_TOOLKIT" = "android" ]; then