From 9da62d84ad27ba72caae61b8d25b5ea6633eef4a Mon Sep 17 00:00:00 2001 From: Yuri Date: Fri, 13 Dec 2019 09:20:24 +0000 Subject: [PATCH] Bug 1565597 - Mozilla Remoting implementation for macOS, r=mossop Differential Revision: https://phabricator.services.mozilla.com/D56997 --HG-- extra : moz-landing-system : lando --- toolkit/components/remote/RemoteUtils.h | 15 +- toolkit/components/remote/moz.build | 6 + toolkit/components/remote/nsMacRemoteClient.h | 28 +++ .../components/remote/nsMacRemoteClient.mm | 62 +++++++ toolkit/components/remote/nsMacRemoteServer.h | 30 ++++ .../components/remote/nsMacRemoteServer.mm | 134 ++++++++++++++ toolkit/components/remote/nsRemoteClient.h | 6 +- toolkit/components/remote/nsRemoteService.cpp | 7 + toolkit/components/remote/nsRemoteService.h | 6 +- .../components/remote/nsWinRemoteClient.cpp | 3 +- toolkit/components/remote/nsWinRemoteClient.h | 4 - .../components/remote/nsWinRemoteServer.cpp | 2 +- toolkit/components/remote/nsWinRemoteUtils.h | 18 -- toolkit/moz.configure | 2 +- toolkit/xre/nsNativeAppSupportCocoa.mm | 170 ------------------ 15 files changed, 288 insertions(+), 205 deletions(-) create mode 100644 toolkit/components/remote/nsMacRemoteClient.h create mode 100644 toolkit/components/remote/nsMacRemoteClient.mm create mode 100644 toolkit/components/remote/nsMacRemoteServer.h create mode 100644 toolkit/components/remote/nsMacRemoteServer.mm delete mode 100644 toolkit/components/remote/nsWinRemoteUtils.h diff --git a/toolkit/components/remote/RemoteUtils.h b/toolkit/components/remote/RemoteUtils.h index 47134cabfc8b..e8097faf94e9 100644 --- a/toolkit/components/remote/RemoteUtils.h +++ b/toolkit/components/remote/RemoteUtils.h @@ -3,11 +3,20 @@ * 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 RemoteUtils_h__ -#define RemoteUtils_h__ +#ifndef TOOLKIT_COMPONENTS_REMOTE_REMOTEUTILS_H_ +#define TOOLKIT_COMPONENTS_REMOTE_REMOTEUTILS_H_ + +#include "nsString.h" + +#if defined XP_WIN || defined XP_MACOSX +static void BuildClassName(const char* aProgram, const char* aProfile, + nsString& aClassName) { + aClassName.AppendPrintf("Mozilla_%s_%s_RemoteWindow", aProgram, aProfile); +} +#endif char* ConstructCommandLine(int32_t argc, char** argv, const char* aDesktopStartupID, int* aCommandLineLength); -#endif // RemoteUtils_h__ \ No newline at end of file +#endif // TOOLKIT_COMPONENTS_REMOTE_REMOTEUTILS_H_ \ No newline at end of file diff --git a/toolkit/components/remote/moz.build b/toolkit/components/remote/moz.build index 4226aac67f6b..4453188fec2b 100644 --- a/toolkit/components/remote/moz.build +++ b/toolkit/components/remote/moz.build @@ -32,8 +32,14 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': 'nsWinRemoteClient.cpp', 'nsWinRemoteServer.cpp', ] +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + SOURCES += [ + 'nsMacRemoteClient.mm', + 'nsMacRemoteServer.mm', + ] LOCAL_INCLUDES += [ '../../profile', + '../../xre', ] FINAL_LIBRARY = 'xul' diff --git a/toolkit/components/remote/nsMacRemoteClient.h b/toolkit/components/remote/nsMacRemoteClient.h new file mode 100644 index 000000000000..71bf8e7f5510 --- /dev/null +++ b/toolkit/components/remote/nsMacRemoteClient.h @@ -0,0 +1,28 @@ +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* 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 TOOLKIT_COMPONENTS_REMOTE_NSMACREMOTECLIENT_H_ +#define TOOLKIT_COMPONENTS_REMOTE_NSMACREMOTECLIENT_H_ + +#import + +#include "nscore.h" +#include "nsRemoteClient.h" + +class nsMacRemoteClient : public nsRemoteClient { + public: + virtual ~nsMacRemoteClient() = default; + + nsresult Init() override; + + nsresult SendCommandLine(const char* aProgram, const char* aProfile, + int32_t argc, char** argv, + const char* aDesktopStartupID, char** aResponse, + bool* aSucceeded) override; +}; + +#endif // TOOLKIT_COMPONENTS_REMOTE_NSMACREMOTECLIENT_H_ diff --git a/toolkit/components/remote/nsMacRemoteClient.mm b/toolkit/components/remote/nsMacRemoteClient.mm new file mode 100644 index 000000000000..314eebc030e8 --- /dev/null +++ b/toolkit/components/remote/nsMacRemoteClient.mm @@ -0,0 +1,62 @@ +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* 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 +#import + +#include + +#include "MacAutoreleasePool.h" +#include "nsMacRemoteClient.h" +#include "RemoteUtils.h" + +using namespace mozilla; + +nsresult nsMacRemoteClient::Init() { return NS_OK; } + +nsresult nsMacRemoteClient::SendCommandLine(const char* aProgram, const char* aProfile, + int32_t argc, char** argv, + const char* aDesktopStartupID, char** aResponse, + bool* aSucceeded) { + mozilla::MacAutoreleasePool pool; + + *aSucceeded = false; + + nsString className; + BuildClassName(aProgram, aProfile, className); + NSString* serverNameString = + [NSString stringWithCharacters:reinterpret_cast(className.get()) + length:className.Length()]; + + CFMessagePortRef messageServer = CFMessagePortCreateRemote(0, (CFStringRef)serverNameString); + + if (messageServer) { + // Getting current process directory + char cwdPtr[MAXPATHLEN + 1]; + getcwd(cwdPtr, MAXPATHLEN + 1); + + NSMutableArray* argumentsArray = [NSMutableArray array]; + for (int i = 0; i < argc; i++) { + NSString* argument = [NSString stringWithUTF8String:argv[i]]; + [argumentsArray addObject:argument]; + } + NSDictionary* dict = @{@"args" : argumentsArray}; + + NSData* data = [NSKeyedArchiver archivedDataWithRootObject:dict]; + + CFMessagePortSendRequest(messageServer, 0, (CFDataRef)data, 10.0, 0.0, NULL, NULL); + + CFMessagePortInvalidate(messageServer); + CFRelease(messageServer); + *aSucceeded = true; + + } else { + // Remote Server not found. Doing nothing. + } + + return NS_OK; +} diff --git a/toolkit/components/remote/nsMacRemoteServer.h b/toolkit/components/remote/nsMacRemoteServer.h new file mode 100644 index 000000000000..0e2bda8a32bf --- /dev/null +++ b/toolkit/components/remote/nsMacRemoteServer.h @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=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/. */ + +#ifndef TOOLKIT_COMPONENTS_REMOTE_NSMACREMOTESERVER_H_ +#define TOOLKIT_COMPONENTS_REMOTE_NSMACREMOTESERVER_H_ + +#import + +#include "nsRemoteServer.h" + +class nsMacRemoteServer final : public nsRemoteServer { + public: + nsMacRemoteServer() = default; + ~nsMacRemoteServer() override { Shutdown(); } + + nsresult Startup(const char* aAppName, const char* aProfileName) override; + void Shutdown() override; + + void HandleCommandLine(CFDataRef aData); + + private: + CFRunLoopSourceRef mRunLoopSource; + CFMessagePortRef mMessageServer; +}; + +#endif // TOOLKIT_COMPONENTS_REMOTE_NSMACREMOTESERVER_H_ diff --git a/toolkit/components/remote/nsMacRemoteServer.mm b/toolkit/components/remote/nsMacRemoteServer.mm new file mode 100644 index 000000000000..0936d4d2fd76 --- /dev/null +++ b/toolkit/components/remote/nsMacRemoteServer.mm @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=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/. */ + +#import +#import + +#include "MacAutoreleasePool.h" +#include "nsCOMPtr.h" +#include "nsIComponentManager.h" +#include "nsIServiceManager.h" +#include "nsIWindowMediator.h" +#include "nsIWidget.h" +#include "nsICommandLineRunner.h" +#include "nsICommandLine.h" +#include "nsCommandLine.h" +#include "nsIDocShell.h" +#include "nsMacRemoteServer.h" +#include "nsXPCOM.h" +#include "RemoteUtils.h" + +CFDataRef messageServerCallback(CFMessagePortRef aLocal, int32_t aMsgid, CFDataRef aData, + void* aInfo) { + // One of the clients submitted a structure. + static_cast(aInfo)->HandleCommandLine(aData); + + return NULL; +} + +// aData contains serialized Dictionary, which in turn contains command line arguments +void nsMacRemoteServer::HandleCommandLine(CFDataRef aData) { + mozilla::MacAutoreleasePool pool; + + if (aData) { + NSDictionary* dict = [NSKeyedUnarchiver unarchiveObjectWithData:(NSData*)aData]; + if (dict && [dict isKindOfClass:[NSDictionary class]]) { + NSArray* args = dict[@"args"]; + if (!args) { + NS_ERROR("Wrong parameters passed to the Remote Server"); + return; + } + + nsCOMPtr cmdLine(new nsCommandLine()); + + // Converting Objective-C array into a C array, + // which nsICommandLineRunner understands. + int argc = [args count]; + const char** argv = new const char*[argc]; + for (int i = 0; i < argc; i++) { + const char* arg = [[args objectAtIndex:i] UTF8String]; + argv[i] = arg; + } + + nsresult rv = cmdLine->Init(argc, argv, nullptr, nsICommandLine::STATE_REMOTE_AUTO); + + // Cleaning up C array. + delete[] argv; + + if (NS_FAILED(rv)) { + NS_ERROR("Error initializing command line."); + return; + } + + // Processing the command line, passed from a remote instance + // in the current instance. + cmdLine->Run(); + + // And bring the app's window to front. + [[NSRunningApplication currentApplication] + activateWithOptions:NSApplicationActivateIgnoringOtherApps]; + } + } +} + +nsresult nsMacRemoteServer::Startup(const char* aAppName, const char* aProfileName) { + // This is the first instance ever. + // Let's register a notification listener here, + // In case future instances would want to notify us about command line arguments + // passed to them. Note, that if mozilla process is restarting, we still need to + // register for notifications. + + mozilla::MacAutoreleasePool pool; + + nsString className; + BuildClassName(aAppName, aProfileName, className); + + NSString* serverNameString = + [NSString stringWithCharacters:reinterpret_cast(className.get()) + length:className.Length()]; + + CFMessagePortContext context; + context.copyDescription = NULL; + context.info = this; + context.release = NULL; + context.retain = NULL; + context.version = NULL; + mMessageServer = CFMessagePortCreateLocal(NULL, (CFStringRef)serverNameString, + messageServerCallback, &context, NULL); + if (!mMessageServer) { + return NS_ERROR_FAILURE; + } + mRunLoopSource = CFMessagePortCreateRunLoopSource(NULL, mMessageServer, 0); + if (!mRunLoopSource) { + CFRelease(mMessageServer); + mMessageServer = NULL; + return NS_ERROR_FAILURE; + } + CFRunLoopRef runLoop = CFRunLoopGetMain(); + CFRunLoopAddSource(runLoop, mRunLoopSource, kCFRunLoopDefaultMode); + + return NS_OK; +} + +void nsMacRemoteServer::Shutdown() { + // 1) Invalidate server connection + if (mMessageServer) { + CFMessagePortInvalidate(mMessageServer); + } + + // 2) Release run loop source + if (mRunLoopSource) { + CFRelease(mRunLoopSource); + mRunLoopSource = NULL; + } + + // 3) Release server connection + if (mMessageServer) { + CFRelease(mMessageServer); + mMessageServer = NULL; + } +} diff --git a/toolkit/components/remote/nsRemoteClient.h b/toolkit/components/remote/nsRemoteClient.h index 4f8050a2374f..d07c8153633e 100644 --- a/toolkit/components/remote/nsRemoteClient.h +++ b/toolkit/components/remote/nsRemoteClient.h @@ -5,8 +5,8 @@ * 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 nsRemoteClient_h__ -#define nsRemoteClient_h__ +#ifndef TOOLKIT_COMPONENTS_REMOTE_NSREMOTECLIENT_H_ +#define TOOLKIT_COMPONENTS_REMOTE_NSREMOTECLIENT_H_ #include "nscore.h" @@ -59,4 +59,4 @@ class nsRemoteClient { char** aResponse, bool* aSucceeded) = 0; }; -#endif // nsRemoteClient_h__ +#endif // TOOLKIT_COMPONENTS_REMOTE_NSREMOTECLIENT_H_ diff --git a/toolkit/components/remote/nsRemoteService.cpp b/toolkit/components/remote/nsRemoteService.cpp index 604b0377e117..a45d209bcffa 100644 --- a/toolkit/components/remote/nsRemoteService.cpp +++ b/toolkit/components/remote/nsRemoteService.cpp @@ -20,6 +20,9 @@ #elif defined(XP_WIN) # include "nsWinRemoteServer.h" # include "nsWinRemoteClient.h" +#elif defined(XP_DARWIN) +# include "nsMacRemoteServer.h" +# include "nsMacRemoteClient.h" #endif #include "nsRemoteService.h" @@ -107,6 +110,8 @@ RemoteResult nsRemoteService::StartClient(const char* aDesktopStartupID) { } #elif defined(XP_WIN) client = new nsWinRemoteClient(); +#elif defined(XP_DARWIN) + client = new nsMacRemoteClient(); #else return REMOTE_NOT_FOUND; #endif @@ -154,6 +159,8 @@ void nsRemoteService::StartupServer() { } #elif defined(XP_WIN) mRemoteServer = MakeUnique(); +#elif defined(XP_DARWIN) + mRemoteServer = MakeUnique(); #else return; #endif diff --git a/toolkit/components/remote/nsRemoteService.h b/toolkit/components/remote/nsRemoteService.h index 019117308f17..7b9bdba85061 100644 --- a/toolkit/components/remote/nsRemoteService.h +++ b/toolkit/components/remote/nsRemoteService.h @@ -5,8 +5,8 @@ * 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 __nsRemoteService_h__ -#define __nsRemoteService_h__ +#ifndef TOOLKIT_COMPONENTS_REMOTE_NSREMOTESERVER_H_ +#define TOOLKIT_COMPONENTS_REMOTE_NSREMOTESERVER_H_ #include "nsRemoteServer.h" #include "nsIObserver.h" @@ -46,4 +46,4 @@ class nsRemoteService final : public nsIObserver { nsCString mProfile; }; -#endif // __nsRemoteService_h__ +#endif // TOOLKIT_COMPONENTS_REMOTE_NSREMOTESERVER_H_ diff --git a/toolkit/components/remote/nsWinRemoteClient.cpp b/toolkit/components/remote/nsWinRemoteClient.cpp index bb126b0a7591..cc81ebc6906c 100644 --- a/toolkit/components/remote/nsWinRemoteClient.cpp +++ b/toolkit/components/remote/nsWinRemoteClient.cpp @@ -6,9 +6,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsWinRemoteClient.h" -#include "nsWinRemoteUtils.h" - #include +#include "RemoteUtils.h" using namespace mozilla; diff --git a/toolkit/components/remote/nsWinRemoteClient.h b/toolkit/components/remote/nsWinRemoteClient.h index f17e3c9dfe0b..a842132abf7e 100644 --- a/toolkit/components/remote/nsWinRemoteClient.h +++ b/toolkit/components/remote/nsWinRemoteClient.h @@ -11,10 +11,6 @@ #include "nscore.h" #include "nsRemoteClient.h" -/** - * Pure-virtual common base class for remoting implementations. - */ - class nsWinRemoteClient : public nsRemoteClient { public: virtual ~nsWinRemoteClient() = default; diff --git a/toolkit/components/remote/nsWinRemoteServer.cpp b/toolkit/components/remote/nsWinRemoteServer.cpp index ed34f3c76afd..0b6798246ed3 100644 --- a/toolkit/components/remote/nsWinRemoteServer.cpp +++ b/toolkit/components/remote/nsWinRemoteServer.cpp @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsWinRemoteServer.h" -#include "nsWinRemoteUtils.h" +#include "RemoteUtils.h" #include "nsCOMPtr.h" #include "nsXPCOM.h" #include "nsPIDOMWindow.h" diff --git a/toolkit/components/remote/nsWinRemoteUtils.h b/toolkit/components/remote/nsWinRemoteUtils.h deleted file mode 100644 index d0092b472afd..000000000000 --- a/toolkit/components/remote/nsWinRemoteUtils.h +++ /dev/null @@ -1,18 +0,0 @@ -/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:expandtab:shiftwidth=4:tabstop=4: - */ -/* 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 nsWinRemoteUtils_h__ -#define nsWinRemoteUtils_h__ - -#include "nsString.h" - -static void BuildClassName(const char* aProgram, const char* aProfile, - nsString& aClassName) { - aClassName.AppendPrintf("Mozilla_%s_%s_RemoteWindow", aProgram, aProfile); -} - -#endif // nsWinRemoteUtils_h__ diff --git a/toolkit/moz.configure b/toolkit/moz.configure index b0ca8a2a89af..0a630ccb819a 100644 --- a/toolkit/moz.configure +++ b/toolkit/moz.configure @@ -1763,7 +1763,7 @@ with only_when(compile_environment & target_is_windows): @depends(toolkit) def has_remote(toolkit): - if toolkit in ('gtk', 'windows'): + if toolkit in ('gtk', 'windows', 'cocoa'): return True set_config('MOZ_HAS_REMOTE', has_remote) diff --git a/toolkit/xre/nsNativeAppSupportCocoa.mm b/toolkit/xre/nsNativeAppSupportCocoa.mm index 60ba0b4b2b4b..c43388b74281 100644 --- a/toolkit/xre/nsNativeAppSupportCocoa.mm +++ b/toolkit/xre/nsNativeAppSupportCocoa.mm @@ -50,61 +50,6 @@ nsresult GetNativeWindowPointerFromDOMWindow(mozIDOMWindowProxy* a_window, return NS_OK; } -// Essentially this notification handler implements the -// "Mozilla remote" functionality on Mac, handing command line arguments (passed -// to a newly launched process) to another copy of the current process that was -// already running (which had registered the handler for this notification). All -// other new copies just broadcast this notification and quit (unless -no-remote -// was specified in either of these processes), making the original process handle -// the arguments passed to this handler. -void remoteClientNotificationCallback(CFNotificationCenterRef aCenter, void* aObserver, - CFStringRef aName, const void* aObject, - CFDictionaryRef aUserInfo) { - // Autorelease pool to prevent memory leaks, in case there is no outer pool. - mozilla::MacAutoreleasePool pool; - NSDictionary* userInfoDict = (__bridge NSDictionary*)aUserInfo; - if (userInfoDict && [userInfoDict objectForKey:@"commandLineArgs"] && - [userInfoDict objectForKey:@"senderPath"]) { - NSString* senderPath = [userInfoDict objectForKey:@"senderPath"]; - if (![senderPath isEqual:[[NSBundle mainBundle] bundlePath]]) { - // The caller is not the process at the same path as we are at. Skipping. - return; - } - - NSArray* args = [userInfoDict objectForKey:@"commandLineArgs"]; - nsCOMPtr cmdLine(new nsCommandLine()); - - // Converting Objective-C array into a C array, - // which nsICommandLineRunner understands. - int argc = [args count]; - const char** argv = new const char*[argc]; - for (int i = 0; i < argc; i++) { - const char* arg = [[args objectAtIndex:i] UTF8String]; - argv[i] = arg; - } - - // We're not currently passing the working dir as third argument because it - // does not appear to be required. - nsresult rv = cmdLine->Init(argc, argv, nullptr, nsICommandLine::STATE_REMOTE_AUTO); - - // Cleaning up C array. - delete[] argv; - - if (NS_FAILED(rv)) { - NS_ERROR("Error initializing command line."); - return; - } - - // Processing the command line, passed from a remote instance - // in the current instance. - cmdLine->Run(); - - // And bring the app's window to front. - [[NSRunningApplication currentApplication] - activateWithOptions:NSApplicationActivateIgnoringOtherApps]; - } -} - class nsNativeAppSupportCocoa : public nsNativeAppSupportBase { public: nsNativeAppSupportCocoa() : mCanShowUI(false) {} @@ -141,121 +86,6 @@ NS_IMETHODIMP nsNativeAppSupportCocoa::Start(bool* _retval) { *_retval = true; - // Here are the "special" CLI arguments that we can expect to be passed that - // should alter the default "hand args list to remote process and quit" algorithm: - // -headless : was already handled on macOS (allowing running multiple instances - // of the app), meaning this patch shouldn't break it. - // -no-remote : should always proceed, creating a second instance (which will - // fail on macOS, showing a MessageBox "Only one instance can be run at a time", - // unless a different profile dir path is specified). - // The rest of the arguments should be either passed on to - // the original running process (exiting the current process), or be processed by - // the current process (if -no-remote is specified). - - mozilla::MacAutoreleasePool pool; - - NSArray* arguments = [[NSProcessInfo processInfo] arguments]; - BOOL shallProceedLikeNoRemote = NO; - for (NSString* arg in arguments) { - if ([arg isEqualToString:@"-no-remote"] || [arg isEqualToString:@"--no-remote"] || - [arg isEqualToString:@"-headless"] || [arg isEqualToString:@"--headless"] || - [arg isEqualToString:@"-createProfile"] || [arg isEqualToString:@"--createProfile"]) { - shallProceedLikeNoRemote = YES; - break; - } - } - - BOOL mozillaRestarting = NO; - if ([[[[NSProcessInfo processInfo] environment] objectForKey:@"MOZ_APP_RESTART"] - isEqualToString:@"1"]) { - // Update process completed or restarting the app for another reason. - // Triggered by an old instance that just quit. - mozillaRestarting = YES; - } - - // Apart from -no-remote, the user can specify an env variable - // MOZ_NO_REMOTE=1, which makes it behave the same way. - // Also, to make sure the tests do not break, - // if env var MOZ_TEST_PROCESS_UPDATES is present, it means the test is running. - // We should proceed as if -no-remote had been specified. - if (shallProceedLikeNoRemote == NO) { - NSDictionary* environmentVariables = [[NSProcessInfo processInfo] environment]; - for (NSString* key in [environmentVariables allKeys]) { - if ([key isEqualToString:@"MOZ_NO_REMOTE"] && - [environmentVariables[key] isEqualToString:@"1"]) { - shallProceedLikeNoRemote = YES; - break; - } - } - } - - // Now that we have handled no-remote-like arguments, at this point: - // 1) Either only the first instance of the process has been launched in any way - // (.app double click, "open", "open -n", invoking executable in Terminal, etc. - // 2) Or the process has been launched with a "macos single instance" mechanism - // override (using "open -n" OR directly by invoking the executable in Terminal - // instead of clicking the .app bundle's icon, etc.). - - // So, let's check if this is the first instance ever of the process for the - // current user. - NSString* notificationName = [[[NSBundle mainBundle] bundleIdentifier] - stringByAppendingString:@".distributedNotification.commandLineArgs"]; - - BOOL runningInstanceFound = NO; - if (!shallProceedLikeNoRemote) { - // We check for other running instances only if -no-remote was not specified. - // The check is needed so the marAppApplyUpdateSuccess.js test doesn't fail on next call. - NSArray* appsWithMatchingId = [NSRunningApplication - runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]]; - NSString* currentAppBundlePath = [[NSBundle mainBundle] bundlePath]; - NSRunningApplication* currentApp = [NSRunningApplication currentApplication]; - for (NSRunningApplication* app in appsWithMatchingId) { - if ([currentAppBundlePath isEqual:[[app bundleURL] path]] && ![currentApp isEqual:app]) { - runningInstanceFound = YES; - break; - } - } - } - - if (!shallProceedLikeNoRemote && !mozillaRestarting && runningInstanceFound) { - // There is another instance of this app already running! - NSArray* arguments = [[NSProcessInfo processInfo] arguments]; - NSString* senderPath = [[NSBundle mainBundle] bundlePath]; - CFDictionaryRef userInfoDict = - (__bridge CFDictionaryRef) @{@"commandLineArgs" : arguments, @"senderPath" : senderPath}; - - // This code is shared between Firefox, Thunderbird and other Mozilla products. - // So we need a notification name that is unique to the product, so we - // do not send a notification to Firefox from Thunderbird and so on. I am using - // bundle Id (assuming all Mozilla products come wrapped in .app bundles) - - // it should be unique - // (e.g., org.mozilla.firefox.distributedNotification.commandLineArgs for Firefox). - // We also need to make sure the notifications are "local" to the current user, - // so we do not pass it on to perhaps another running Thunderbird by another - // logged in user. Distributed notifications is the best candidate - // (while darwin notifications ignore the user context). - CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(), - (__bridge CFStringRef)notificationName, NULL, userInfoDict, - true); - - // Do not continue start up sequence for this process - just self-terminate, - // we already passed the arguments on to the original instance of the process. - *_retval = false; - } else { - // This is the first instance ever (or launched as -no-remote)! - // Let's register a notification listener here, - // In case future instances would want to notify us about command line arguments - // passed to them. Note, that if mozilla process is restarting, we still need to - // register for notifications. - CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), NULL, - remoteClientNotificationCallback, - (__bridge CFStringRef)notificationName, NULL, - CFNotificationSuspensionBehaviorDeliverImmediately); - - // Continue the start up sequence of this process. - *_retval = true; - } - return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;