From 6616e71c71b85f6b81794dbe10404e74c31bd020 Mon Sep 17 00:00:00 2001 From: Josh Aas Date: Fri, 30 Jul 2010 09:44:51 -0400 Subject: [PATCH] Bug 531552: Force handling of at-launch open-url and open-file Apple Events to happen during command line construction so that we can put them on the command line and don't open unnecessary windows. r=benwa --- toolkit/xre/MacApplicationDelegate.mm | 65 +++++++++++++++++++++++-- toolkit/xre/nsAppRunner.cpp | 7 +-- toolkit/xre/nsCommandLineServiceMac.cpp | 41 ++++++++++++++-- toolkit/xre/nsCommandLineServiceMac.h | 5 ++ 4 files changed, 107 insertions(+), 11 deletions(-) diff --git a/toolkit/xre/MacApplicationDelegate.mm b/toolkit/xre/MacApplicationDelegate.mm index dd0ba87d14b..a8e43228de0 100644 --- a/toolkit/xre/MacApplicationDelegate.mm +++ b/toolkit/xre/MacApplicationDelegate.mm @@ -64,6 +64,21 @@ #include "nsIStandaloneNativeMenu.h" #include "nsILocalFileMac.h" #include "nsString.h" +#include "nsCommandLineServiceMac.h" + +class AutoAutoreleasePool { +public: + AutoAutoreleasePool() + { + mLocalPool = [[NSAutoreleasePool alloc] init]; + } + ~AutoAutoreleasePool() + { + [mLocalPool release]; + } +private: + NSAutoreleasePool *mLocalPool; +}; @interface MacApplicationDelegate : NSObject { @@ -92,7 +107,7 @@ SetupMacApplicationDelegate() // this is called during startup, outside an event loop, and therefore // needs an autorelease pool to avoid cocoa object leakage (bug 559075) - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + AutoAutoreleasePool pool; // This call makes it so that application:openFile: doesn't get bogus calls // from Cocoa doing its own parsing of the argument string. And yes, we need @@ -104,8 +119,6 @@ SetupMacApplicationDelegate() MacApplicationDelegate *delegate = [[MacApplicationDelegate alloc] init]; [NSApp setDelegate:delegate]; - [pool release]; - NS_OBJC_END_TRY_ABORT_BLOCK; } @@ -128,6 +141,11 @@ SetupMacApplicationDelegate() forEventClass:'WWW!' andEventID:'OURL']; + [aeMgr setEventHandler:self + andSelector:@selector(handleAppleEvent:withReplyEvent:) + forEventClass:kCoreEventClass + andEventID:kAEOpenDocuments]; + if (![NSApp windowsMenu]) { // If the application has a windows menu, it will keep it up to date and // prepend the window list to the Dock menu automatically. @@ -148,6 +166,7 @@ SetupMacApplicationDelegate() NSAppleEventManager *aeMgr = [NSAppleEventManager sharedAppleEventManager]; [aeMgr removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL]; [aeMgr removeEventHandlerForEventClass:'WWW!' andEventID:'OURL']; + [aeMgr removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEOpenDocuments]; [super dealloc]; NS_OBJC_END_TRY_ABORT_BLOCK; @@ -175,10 +194,20 @@ SetupMacApplicationDelegate() { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - NSString *escapedPath = [filename stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSURL *url = [NSURL fileURLWithPath:filename]; + if (!url) + return NO; + + NSString *urlString = [url absoluteString]; + if (!urlString) + return NO; + + // Add the URL to any command line we're currently setting up. + if (CommandLineServiceMac::AddURLToCurrentCommandLine([urlString UTF8String])) + return YES; nsCOMPtr inFile; - nsresult rv = NS_NewLocalFileWithCFURL((CFURLRef)[NSURL URLWithString:escapedPath], PR_TRUE, getter_AddRefs(inFile)); + nsresult rv = NS_NewLocalFileWithCFURL((CFURLRef)url, PR_TRUE, getter_AddRefs(inFile)); if (NS_FAILED(rv)) return NO; @@ -307,6 +336,8 @@ SetupMacApplicationDelegate() if (!event) return; + AutoAutoreleasePool pool; + if (([event eventClass] == kInternetEventClass && [event eventID] == kAEGetURL) || ([event eventClass] == 'WWW!' && [event eventID] == 'OURL')) { NSString* urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; @@ -320,6 +351,10 @@ SetupMacApplicationDelegate() return; } + // Add the URL to any command line we're currently setting up. + if (CommandLineServiceMac::AddURLToCurrentCommandLine([urlString UTF8String])) + return; + nsCOMPtr cmdLine(do_CreateInstance("@mozilla.org/toolkit/command-line;1")); if (!cmdLine) { NS_ERROR("Couldn't create command line!"); @@ -335,6 +370,26 @@ SetupMacApplicationDelegate() return; rv = cmdLine->Run(); } + else if ([event eventClass] == kCoreEventClass && [event eventID] == kAEOpenDocuments) { + NSAppleEventDescriptor* fileListDescriptor = [event paramDescriptorForKeyword:keyDirectObject]; + if (!fileListDescriptor) + return; + + // Descriptor list indexing is one-based... + NSInteger numberOfFiles = [fileListDescriptor numberOfItems]; + for (NSInteger i = 1; i <= numberOfFiles; i++) { + NSString* urlString = [[fileListDescriptor descriptorAtIndex:i] stringValue]; + if (!urlString) + continue; + + // We need a path, not a URL + NSURL* url = [NSURL URLWithString:urlString]; + if (!url) + continue; + + [self application:NSApp openFile:[url path]]; + } + } } @end diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 3b8a13c6e5c..cd91eb8a693 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -3565,6 +3565,10 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) #endif #ifdef XP_MACOSX + // Set up ability to respond to system (Apple) events. This must be + // done before setting up the command line service. + SetupMacApplicationDelegate(); + // we re-initialize the command-line service and do appleevents munging // after we are sure that we're not restarting cmdLine = do_CreateInstance("@mozilla.org/toolkit/command-line;1"); @@ -3575,9 +3579,6 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) rv = cmdLine->Init(gArgc, gArgv, workingDir, nsICommandLine::STATE_INITIAL_LAUNCH); NS_ENSURE_SUCCESS(rv, 1); - - // Set up ability to respond to system (Apple) events. - SetupMacApplicationDelegate(); #endif MOZ_SPLASHSCREEN_UPDATE(70); diff --git a/toolkit/xre/nsCommandLineServiceMac.cpp b/toolkit/xre/nsCommandLineServiceMac.cpp index b24dca3fc63..cd3649ab825 100644 --- a/toolkit/xre/nsCommandLineServiceMac.cpp +++ b/toolkit/xre/nsCommandLineServiceMac.cpp @@ -51,6 +51,8 @@ static char** sArgs = NULL; static int sArgsAllocated = 0; static int sArgsUsed = 0; +static PRBool sBuildingCommandLine = PR_FALSE; + void AddToCommandLine(const char* inArgText) { if (sArgsUsed >= sArgsAllocated - 1) { @@ -81,6 +83,8 @@ void SetupMacCommandLine(int& argc, char**& argv, PRBool forRestart) sArgs[0] = NULL; sArgsUsed = 0; + sBuildingCommandLine = PR_TRUE; + // Copy args, stripping anything we don't want. for (int arg = 0; arg < argc; arg++) { char* flag = argv[arg]; @@ -89,10 +93,27 @@ void SetupMacCommandLine(int& argc, char**& argv, PRBool forRestart) AddToCommandLine(flag); } + // Force processing of any pending Apple Events while we're building the + // command line. The handlers will append to the command line rather than + // act directly so there is no chance we'll process them during a XUL window + // load and accidentally open unnecessary windows and home pages. + const EventTypeSpec kAppleEventList[] = { + { kEventClassAppleEvent, kEventAppleEvent }, + }; + EventRef carbonEvent; + while (::ReceiveNextEvent(GetEventTypeCount(kAppleEventList), + kAppleEventList, + kEventDurationNoWait, + PR_TRUE, + &carbonEvent) == noErr) { + ::AEProcessEvent(carbonEvent); + ::ReleaseEvent(carbonEvent); + } + + // If the process will be relaunched, the child should be in the foreground + // if the parent is in the foreground. This will be communicated in a + // command-line argument to the child. if (forRestart) { - // If the process will be relaunched, the child should be in the foreground - // if the parent is in the foreground. This will be communicated in a - // command-line argument to the child. Boolean isForeground = false; ProcessSerialNumber psnSelf, psnFront; if (::GetCurrentProcess(&psnSelf) == noErr && @@ -103,8 +124,22 @@ void SetupMacCommandLine(int& argc, char**& argv, PRBool forRestart) } } + sBuildingCommandLine = PR_FALSE; + argc = sArgsUsed; argv = sArgs; } +PRBool AddURLToCurrentCommandLine(const char* aURL) +{ + if (!sBuildingCommandLine) { + return PR_FALSE; + } + + AddToCommandLine("-url"); + AddToCommandLine(aURL); + + return PR_TRUE; +} + } // namespace CommandLineServiceMac diff --git a/toolkit/xre/nsCommandLineServiceMac.h b/toolkit/xre/nsCommandLineServiceMac.h index af354a520ef..94bacc2b1d6 100644 --- a/toolkit/xre/nsCommandLineServiceMac.h +++ b/toolkit/xre/nsCommandLineServiceMac.h @@ -42,6 +42,11 @@ namespace CommandLineServiceMac { void SetupMacCommandLine(int& argc, char**& argv, PRBool forRestart); + + // Add a URL to the command line currently being set up via + // SetupMacCommandLine. Returns false if no command line is + // being set up or the addition fails for any other reason. + PRBool AddURLToCurrentCommandLine(const char* aURL); } #endif // nsCommandLineServiceMac_h_