toolkit/xre/MacApplicationDelegate.mm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 // NSApplication delegate for Mac OS X Cocoa API.
     8 // As of 10.4 Tiger, the system can send six kinds of Apple Events to an application;
     9 // a well-behaved XUL app should have some kind of handling for all of them.
    10 //
    11 // See http://developer.apple.com/documentation/Cocoa/Conceptual/ScriptableCocoaApplications/SApps_handle_AEs/chapter_11_section_3.html for details.
    13 #import <Cocoa/Cocoa.h>
    14 #import <Carbon/Carbon.h>
    16 #include "nsCOMPtr.h"
    17 #include "nsINativeAppSupport.h"
    18 #include "nsAppRunner.h"
    19 #include "nsComponentManagerUtils.h"
    20 #include "nsIServiceManager.h"
    21 #include "nsServiceManagerUtils.h"
    22 #include "nsIAppStartup.h"
    23 #include "nsIObserverService.h"
    24 #include "nsISupportsPrimitives.h"
    25 #include "nsObjCExceptions.h"
    26 #include "nsIFile.h"
    27 #include "nsDirectoryServiceDefs.h"
    28 #include "nsICommandLineRunner.h"
    29 #include "nsIMacDockSupport.h"
    30 #include "nsIStandaloneNativeMenu.h"
    31 #include "nsILocalFileMac.h"
    32 #include "nsString.h"
    33 #include "nsCommandLineServiceMac.h"
    35 class AutoAutoreleasePool {
    36 public:
    37   AutoAutoreleasePool()
    38   {
    39     mLocalPool = [[NSAutoreleasePool alloc] init];
    40   }
    41   ~AutoAutoreleasePool()
    42   {
    43     [mLocalPool release];
    44   }
    45 private:
    46   NSAutoreleasePool *mLocalPool;
    47 };
    49 @interface MacApplicationDelegate : NSObject
    50 {
    51 }
    53 @end
    55 static bool sProcessedGetURLEvent = false;
    57 @class GeckoNSApplication;
    59 // Methods that can be called from non-Objective-C code.
    61 // This is needed, on relaunch, to force the OS to use the "Cocoa Dock API"
    62 // instead of the "Carbon Dock API".  For more info see bmo bug 377166.
    63 void
    64 EnsureUseCocoaDockAPI()
    65 {
    66   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
    68   [GeckoNSApplication sharedApplication];
    70   NS_OBJC_END_TRY_ABORT_BLOCK;
    71 }
    73 void
    74 SetupMacApplicationDelegate()
    75 {
    76   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
    78   // this is called during startup, outside an event loop, and therefore
    79   // needs an autorelease pool to avoid cocoa object leakage (bug 559075)
    80   AutoAutoreleasePool pool;
    82   // Ensure that ProcessPendingGetURLAppleEvents() doesn't regress bug 377166.
    83   [GeckoNSApplication sharedApplication];
    85   // This call makes it so that application:openFile: doesn't get bogus calls
    86   // from Cocoa doing its own parsing of the argument string. And yes, we need
    87   // to use a string with a boolean value in it. That's just how it works.
    88   [[NSUserDefaults standardUserDefaults] setObject:@"NO"
    89                                             forKey:@"NSTreatUnknownArgumentsAsOpen"];
    91   // Create the delegate. This should be around for the lifetime of the app.
    92   MacApplicationDelegate *delegate = [[MacApplicationDelegate alloc] init];
    93   [NSApp setDelegate:delegate];
    95   NS_OBJC_END_TRY_ABORT_BLOCK;
    96 }
    98 // Indirectly make the OS process any pending GetURL Apple events.  This is
    99 // done via _DPSNextEvent() (an undocumented AppKit function called from
   100 // [NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:]).  Apple
   101 // events are only processed if 'dequeue' is 'YES' -- so we need to call
   102 // [NSApplication sendEvent:] on any event that gets returned.  'event' will
   103 // never itself be an Apple event, and it may be 'nil' even when Apple events
   104 // are processed.
   105 void
   106 ProcessPendingGetURLAppleEvents()
   107 {
   108   AutoAutoreleasePool pool;
   109   bool keepSpinning = true;
   110   while (keepSpinning) {
   111     sProcessedGetURLEvent = false;
   112     NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask
   113                                         untilDate:nil
   114                                            inMode:NSDefaultRunLoopMode
   115                                           dequeue:YES];
   116     if (event)
   117       [NSApp sendEvent:event];
   118     keepSpinning = sProcessedGetURLEvent;
   119   }
   120 }
   122 @implementation MacApplicationDelegate
   124 - (id)init
   125 {
   126   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
   128   if ((self = [super init])) {
   129     NSAppleEventManager *aeMgr = [NSAppleEventManager sharedAppleEventManager];
   131     [aeMgr setEventHandler:self
   132                andSelector:@selector(handleAppleEvent:withReplyEvent:)
   133              forEventClass:kInternetEventClass
   134                 andEventID:kAEGetURL];
   136     [aeMgr setEventHandler:self
   137                andSelector:@selector(handleAppleEvent:withReplyEvent:)
   138              forEventClass:'WWW!'
   139                 andEventID:'OURL'];
   141     [aeMgr setEventHandler:self
   142                andSelector:@selector(handleAppleEvent:withReplyEvent:)
   143              forEventClass:kCoreEventClass
   144                 andEventID:kAEOpenDocuments];
   146     if (![NSApp windowsMenu]) {
   147       // If the application has a windows menu, it will keep it up to date and
   148       // prepend the window list to the Dock menu automatically.
   149       NSMenu* windowsMenu = [[NSMenu alloc] initWithTitle:@"Window"];
   150       [NSApp setWindowsMenu:windowsMenu];
   151       [windowsMenu release];
   152     }
   153   }
   154   return self;
   156   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nil);
   157 }
   159 - (void)dealloc
   160 {
   161   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   163   NSAppleEventManager *aeMgr = [NSAppleEventManager sharedAppleEventManager];
   164   [aeMgr removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL];
   165   [aeMgr removeEventHandlerForEventClass:'WWW!' andEventID:'OURL'];
   166   [aeMgr removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEOpenDocuments];
   167   [super dealloc];
   169   NS_OBJC_END_TRY_ABORT_BLOCK;
   170 }
   172 // The method that NSApplication calls upon a request to reopen, such as when
   173 // the Dock icon is clicked and no windows are open. A "visible" window may be
   174 // miniaturized, so we can't skip nsCocoaNativeReOpen() if 'flag' is 'true'.
   175 - (BOOL)applicationShouldHandleReopen:(NSApplication*)theApp hasVisibleWindows:(BOOL)flag
   176 {
   177   nsCOMPtr<nsINativeAppSupport> nas = do_CreateInstance(NS_NATIVEAPPSUPPORT_CONTRACTID);
   178   NS_ENSURE_TRUE(nas, NO);
   180   // Go to the common Carbon/Cocoa reopen method.
   181   nsresult rv = nas->ReOpen();
   182   NS_ENSURE_SUCCESS(rv, NO);
   184   // NO says we don't want NSApplication to do anything else for us.
   185   return NO;
   186 }
   188 // The method that NSApplication calls when documents are requested to be opened.
   189 // It will be called once for each selected document.
   190 - (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename
   191 {
   192   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
   194   NSURL *url = [NSURL fileURLWithPath:filename];
   195   if (!url)
   196     return NO;
   198   NSString *urlString = [url absoluteString];
   199   if (!urlString)
   200     return NO;
   202   // Add the URL to any command line we're currently setting up.
   203   if (CommandLineServiceMac::AddURLToCurrentCommandLine([urlString UTF8String]))
   204     return YES;
   206   nsCOMPtr<nsILocalFileMac> inFile;
   207   nsresult rv = NS_NewLocalFileWithCFURL((CFURLRef)url, true, getter_AddRefs(inFile));
   208   if (NS_FAILED(rv))
   209     return NO;
   211   nsCOMPtr<nsICommandLineRunner> cmdLine(do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
   212   if (!cmdLine) {
   213     NS_ERROR("Couldn't create command line!");
   214     return NO;
   215   }
   217   nsCString filePath;
   218   rv = inFile->GetNativePath(filePath);
   219   if (NS_FAILED(rv))
   220     return NO;
   222   nsCOMPtr<nsIFile> workingDir;
   223   rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(workingDir));
   224   if (NS_FAILED(rv))
   225     return NO;
   227   const char *argv[3] = {nullptr, "-file", filePath.get()};
   228   rv = cmdLine->Init(3, argv, workingDir, nsICommandLine::STATE_REMOTE_EXPLICIT);
   229   if (NS_FAILED(rv))
   230     return NO;
   232   if (NS_SUCCEEDED(cmdLine->Run()))
   233     return YES;
   235   return NO;
   237   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
   238 }
   240 // The method that NSApplication calls when documents are requested to be printed
   241 // from the Finder (under the "File" menu).
   242 // It will be called once for each selected document.
   243 - (BOOL)application:(NSApplication*)theApplication printFile:(NSString*)filename
   244 {
   245   return NO;
   246 }
   248 // Create the menu that shows up in the Dock.
   249 - (NSMenu*)applicationDockMenu:(NSApplication*)sender
   250 {
   251   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
   253   // Create the NSMenu that will contain the dock menu items.
   254   NSMenu *menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
   255   [menu setAutoenablesItems:NO];
   257   // Add application-specific dock menu items. On error, do not insert the
   258   // dock menu items.
   259   nsresult rv;
   260   nsCOMPtr<nsIMacDockSupport> dockSupport = do_GetService("@mozilla.org/widget/macdocksupport;1", &rv);
   261   if (NS_FAILED(rv) || !dockSupport)
   262     return menu;
   264   nsCOMPtr<nsIStandaloneNativeMenu> dockMenu;
   265   rv = dockSupport->GetDockMenu(getter_AddRefs(dockMenu));
   266   if (NS_FAILED(rv) || !dockMenu)
   267     return menu;
   269   // Determine if the dock menu items should be displayed. This also gives
   270   // the menu the opportunity to update itself before display.
   271   bool shouldShowItems;
   272   rv = dockMenu->MenuWillOpen(&shouldShowItems);
   273   if (NS_FAILED(rv) || !shouldShowItems)
   274     return menu;
   276   // Obtain a copy of the native menu.
   277   NSMenu * nativeDockMenu;
   278   rv = dockMenu->GetNativeMenu(reinterpret_cast<void **>(&nativeDockMenu));
   279   if (NS_FAILED(rv) || !nativeDockMenu)
   280     return menu;
   282   // Loop through the application-specific dock menu and insert its
   283   // contents into the dock menu that we are building for Cocoa.
   284   int numDockMenuItems = [nativeDockMenu numberOfItems];
   285   if (numDockMenuItems > 0) {
   286     if ([menu numberOfItems] > 0)
   287       [menu addItem:[NSMenuItem separatorItem]];
   289     for (int i = 0; i < numDockMenuItems; i++) {
   290       NSMenuItem * itemCopy = [[nativeDockMenu itemAtIndex:i] copy];
   291       [menu addItem:itemCopy];
   292       [itemCopy release];
   293     }
   294   }
   296   return menu;
   298   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
   299 }
   301 // If we don't handle applicationShouldTerminate:, a call to [NSApp terminate:]
   302 // (from the browser or from the OS) can result in an unclean shutdown.
   303 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
   304 {
   305   nsCOMPtr<nsIObserverService> obsServ =
   306            do_GetService("@mozilla.org/observer-service;1");
   307   if (!obsServ)
   308     return NSTerminateNow;
   310   nsCOMPtr<nsISupportsPRBool> cancelQuit =
   311            do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
   312   if (!cancelQuit)
   313     return NSTerminateNow;
   315   cancelQuit->SetData(false);
   316   obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nullptr);
   318   bool abortQuit;
   319   cancelQuit->GetData(&abortQuit);
   320   if (abortQuit)
   321     return NSTerminateCancel;
   323   nsCOMPtr<nsIAppStartup> appService =
   324            do_GetService("@mozilla.org/toolkit/app-startup;1");
   325   if (appService)
   326     appService->Quit(nsIAppStartup::eForceQuit);
   328   return NSTerminateNow;
   329 }
   331 - (void)handleAppleEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent
   332 {
   333   if (!event)
   334     return;
   336   AutoAutoreleasePool pool;
   338   bool isGetURLEvent =
   339     ([event eventClass] == kInternetEventClass && [event eventID] == kAEGetURL);
   340   if (isGetURLEvent)
   341     sProcessedGetURLEvent = true;
   343   if (isGetURLEvent ||
   344       ([event eventClass] == 'WWW!' && [event eventID] == 'OURL')) {
   345     NSString* urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
   347     // don't open chrome URLs
   348     NSString* schemeString = [[NSURL URLWithString:urlString] scheme];
   349     if (!schemeString ||
   350         [schemeString compare:@"chrome"
   351                       options:NSCaseInsensitiveSearch
   352                         range:NSMakeRange(0, [schemeString length])] == NSOrderedSame) {
   353       return;
   354     }
   356     // Add the URL to any command line we're currently setting up.
   357     if (CommandLineServiceMac::AddURLToCurrentCommandLine([urlString UTF8String]))
   358       return;
   360     nsCOMPtr<nsICommandLineRunner> cmdLine(do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
   361     if (!cmdLine) {
   362       NS_ERROR("Couldn't create command line!");
   363       return;
   364     }
   365     nsCOMPtr<nsIFile> workingDir;
   366     nsresult rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(workingDir));
   367     if (NS_FAILED(rv))
   368       return;
   369     const char *argv[3] = {nullptr, "-url", [urlString UTF8String]};
   370     rv = cmdLine->Init(3, argv, workingDir, nsICommandLine::STATE_REMOTE_EXPLICIT);
   371     if (NS_FAILED(rv))
   372       return;
   373     rv = cmdLine->Run();
   374   }
   375   else if ([event eventClass] == kCoreEventClass && [event eventID] == kAEOpenDocuments) {
   376     NSAppleEventDescriptor* fileListDescriptor = [event paramDescriptorForKeyword:keyDirectObject];
   377     if (!fileListDescriptor)
   378       return;
   380     // Descriptor list indexing is one-based...
   381     NSInteger numberOfFiles = [fileListDescriptor numberOfItems];
   382     for (NSInteger i = 1; i <= numberOfFiles; i++) {
   383       NSString* urlString = [[fileListDescriptor descriptorAtIndex:i] stringValue];
   384       if (!urlString)
   385         continue;
   387       // We need a path, not a URL
   388       NSURL* url = [NSURL URLWithString:urlString];
   389       if (!url)
   390         continue;
   392       [self application:NSApp openFile:[url path]];
   393     }
   394   }
   395 }
   397 @end

mercurial