From 8184cbfb06190dec4a334d04c4e80a9e2ac60f99 Mon Sep 17 00:00:00 2001 From: Ted Kim Date: Tue, 17 Oct 2017 16:28:29 +0900 Subject: [PATCH] Implement login helper to manage login item in Mac App Store build --- atom/app/atom_login_helper.mm | 11 +++++ atom/app/resources/mac/loginhelper-Info.plist | 16 ++++++++ atom/browser/browser_mac.mm | 9 +++++ atom/common/platform_util.h | 5 +++ atom/common/platform_util_mac.mm | 27 +++++++++++++ docs/api/app.md | 25 +++++------- .../mac-app-store-submission-guide.md | 4 +- electron.gyp | 40 +++++++++++++++++++ filenames.gypi | 3 ++ 9 files changed, 123 insertions(+), 17 deletions(-) create mode 100644 atom/app/atom_login_helper.mm create mode 100644 atom/app/resources/mac/loginhelper-Info.plist diff --git a/atom/app/atom_login_helper.mm b/atom/app/atom_login_helper.mm new file mode 100644 index 0000000000..327480adac --- /dev/null +++ b/atom/app/atom_login_helper.mm @@ -0,0 +1,11 @@ +#import + +int main(int argc, char* argv[]) { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSArray* pathComponents = [[[NSBundle mainBundle] bundlePath] pathComponents]; + pathComponents = [pathComponents subarrayWithRange:NSMakeRange(0, [pathComponents count] - 4)]; + NSString* path = [NSString pathWithComponents:pathComponents]; + [[NSWorkspace sharedWorkspace] launchApplication:path]; + [pool drain]; + return 0; +} diff --git a/atom/app/resources/mac/loginhelper-Info.plist b/atom/app/resources/mac/loginhelper-Info.plist new file mode 100644 index 0000000000..68348d27a1 --- /dev/null +++ b/atom/app/resources/mac/loginhelper-Info.plist @@ -0,0 +1,16 @@ + + + + + CFBundleIdentifier + ${ATOM_BUNDLE_ID} + CFBundleName + ${PRODUCT_NAME} + CFBundleExecutable + ${PRODUCT_NAME} + CFBundlePackageType + APPL + LSBackgroundOnly + + + diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index d187fa6cf9..d12de2c14c 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -4,6 +4,7 @@ #include "atom/browser/browser.h" +#include "atom/common/platform_util.h" #include "atom/browser/mac/atom_application.h" #include "atom/browser/mac/atom_application_delegate.h" #include "atom/browser/mac/dict_util.h" @@ -193,19 +194,27 @@ bool Browser::UpdateUserActivityState(const std::string& type, Browser::LoginItemSettings Browser::GetLoginItemSettings( const LoginItemSettings& options) { LoginItemSettings settings; +#if defined(MAS_BUILD) + settings.open_at_login = platform_util::GetLoginItemEnabled(); +#else settings.open_at_login = base::mac::CheckLoginItemStatus( &settings.open_as_hidden); settings.restore_state = base::mac::WasLaunchedAsLoginItemRestoreState(); settings.opened_at_login = base::mac::WasLaunchedAsLoginOrResumeItem(); settings.opened_as_hidden = base::mac::WasLaunchedAsHiddenLoginItem(); +#endif return settings; } void Browser::SetLoginItemSettings(LoginItemSettings settings) { +#if defined(MAS_BUILD) + platform_util::SetLoginItemEnabled(settings.open_at_login); +#else if (settings.open_at_login) base::mac::AddToLoginItems(settings.open_as_hidden); else base::mac::RemoveFromLoginItems(); +#endif } std::string Browser::GetExecutableFileVersion() const { diff --git a/atom/common/platform_util.h b/atom/common/platform_util.h index dc4b472358..6fd84056a0 100644 --- a/atom/common/platform_util.h +++ b/atom/common/platform_util.h @@ -57,6 +57,11 @@ bool MoveItemToTrash(const base::FilePath& full_path); void Beep(); +#if defined(OS_MACOSX) +bool GetLoginItemEnabled(); +void SetLoginItemEnabled(bool enabled); +#endif + } // namespace platform_util #endif // ATOM_COMMON_PLATFORM_UTIL_H_ diff --git a/atom/common/platform_util_mac.mm b/atom/common/platform_util_mac.mm index b48ec51c10..c69d05ebbf 100644 --- a/atom/common/platform_util_mac.mm +++ b/atom/common/platform_util_mac.mm @@ -6,6 +6,7 @@ #import #import +#import #include "base/callback.h" #include "base/files/file_path.h" @@ -98,6 +99,10 @@ std::string OpenURL(NSURL* ns_url, bool activate) { return ""; } +NSString* GetLoginHelperBundleIdentifier() { + return [[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:@".loginhelper"]; +} + } // namespace namespace platform_util { @@ -177,4 +182,26 @@ void Beep() { NSBeep(); } +bool GetLoginItemEnabled() { + BOOL enabled = NO; + // SMJobCopyDictionary does not work in sandbox (see rdar://13626319) + CFArrayRef jobs = SMCopyAllJobDictionaries(kSMDomainUserLaunchd); + NSArray* jobs_ = CFBridgingRelease(jobs); + NSString* identifier = GetLoginHelperBundleIdentifier(); + if (jobs_ && [jobs_ count] > 0) { + for (NSDictionary* job in jobs_) { + if ([identifier isEqualToString:[job objectForKey:@"Label"]]) { + enabled = [[job objectForKey:@"OnDemand"] boolValue]; + break; + } + } + } + return enabled; +} + +void SetLoginItemEnabled(bool enabled) { + NSString* identifier = GetLoginHelperBundleIdentifier(); + SMLoginItemSetEnabled((__bridge CFStringRef) identifier, enabled); +} + } // namespace platform_util diff --git a/docs/api/app.md b/docs/api/app.md index f7ff23e3a7..ff37e98986 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -893,30 +893,27 @@ need to pass the same arguments here for `openAtLogin` to be set correctly. Returns `Object`: * `openAtLogin` Boolean - `true` if the app is set to open at login. -* `openAsHidden` Boolean - `true` if the app is set to open as hidden at login. - This setting is only supported on macOS. -* `wasOpenedAtLogin` Boolean - `true` if the app was opened at login - automatically. This setting is only supported on macOS. -* `wasOpenedAsHidden` Boolean - `true` if the app was opened as a hidden login +* `openAsHidden` Boolean _macOS_ - `true` if the app is set to open as hidden at login. + This setting is not available on [MAS builds][mas-builds]. +* `wasOpenedAtLogin` Boolean _macOS_ - `true` if the app was opened at login + automatically. This setting is not available on [MAS builds][mas-builds]. +* `wasOpenedAsHidden` Boolean _macOS_ - `true` if the app was opened as a hidden login item. This indicates that the app should not open any windows at startup. - This setting is only supported on macOS. -* `restoreState` Boolean - `true` if the app was opened as a login item that + This setting is not available on [MAS builds][mas-builds]. +* `restoreState` Boolean _macOS_ - `true` if the app was opened as a login item that should restore the state from the previous session. This indicates that the app should restore the windows that were open the last time the app was - closed. This setting is only supported on macOS. - -**Note:** This API has no effect on [MAS builds][mas-builds]. + closed. This setting is not available on [MAS builds][mas-builds]. ### `app.setLoginItemSettings(settings)` _macOS_ _Windows_ * `settings` Object * `openAtLogin` Boolean (optional) - `true` to open the app at login, `false` to remove the app as a login item. Defaults to `false`. - * `openAsHidden` Boolean (optional) - `true` to open the app as hidden. Defaults to + * `openAsHidden` Boolean (optional) _macOS_ - `true` to open the app as hidden. Defaults to `false`. The user can edit this setting from the System Preferences so `app.getLoginItemStatus().wasOpenedAsHidden` should be checked when the app - is opened to know the current value. This setting is only supported on - macOS. + is opened to know the current value. This setting is not available on [MAS builds][mas-builds]. * `path` String (optional) _Windows_ - The executable to launch at login. Defaults to `process.execPath`. * `args` String[] (optional) _Windows_ - The command-line arguments to pass to @@ -944,8 +941,6 @@ app.setLoginItemSettings({ }) ``` -**Note:** This API has no effect on [MAS builds][mas-builds]. - ### `app.isAccessibilitySupportEnabled()` _macOS_ _Windows_ Returns `Boolean` - `true` if Chrome's accessibility support is enabled, diff --git a/docs/tutorial/mac-app-store-submission-guide.md b/docs/tutorial/mac-app-store-submission-guide.md index 10c59906a2..4f85e347fc 100644 --- a/docs/tutorial/mac-app-store-submission-guide.md +++ b/docs/tutorial/mac-app-store-submission-guide.md @@ -110,6 +110,8 @@ codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP H codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper EH.app/" codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper NP.app/Contents/MacOS/$APP Helper NP" codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/$APP Helper NP.app/" +codesign -s "$APP_KEY" -f --entitlements "$PARENT_PLIST" "$APP_PATH/Contents/Library/LoginItems/$APP Login Helper.app/Contents/MacOS/$APP Login Helper" +codesign -s "$APP_KEY" -f --entitlements "$PARENT_PLIST" "$APP_PATH/Contents/Library/LoginItems/$APP Login Helper.app/" codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$APP_PATH/Contents/MacOS/$APP" codesign -s "$APP_KEY" -f --entitlements "$PARENT_PLIST" "$APP_PATH" @@ -162,8 +164,6 @@ and the following behaviors have been changed: * Video capture may not work for some machines. * Certain accessibility features may not work. * Apps will not be aware of DNS changes. -* APIs for launching apps at login are disabled. See -https://github.com/electron/electron/issues/7312#issuecomment-249479237 Also, due to the usage of app sandboxing, the resources which can be accessed by the app are strictly limited; you can read [App Sandboxing][app-sandboxing] for diff --git a/electron.gyp b/electron.gyp index aa81d93068..dbb38630cc 100644 --- a/electron.gyp +++ b/electron.gyp @@ -122,6 +122,19 @@ }, ], }], + ['mas_build==1', { + 'dependencies': [ + '<(project_name)_login_helper', + ], + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)/<(product_name).app/Contents/Library/LoginItems', + 'files': [ + '<(PRODUCT_DIR)/<(product_name) Login Helper.app', + ], + }, + ], + }], ], }], # OS!="mac" ['OS=="win"', { @@ -566,6 +579,7 @@ '$(SDKROOT)/System/Library/Frameworks/Quartz.framework', '$(SDKROOT)/System/Library/Frameworks/Security.framework', '$(SDKROOT)/System/Library/Frameworks/SecurityInterface.framework', + '$(SDKROOT)/System/Library/Frameworks/ServiceManagement.framework', ], }, 'mac_bundle': 1, @@ -684,6 +698,32 @@ ], }, }, # target helper + { + 'target_name': '<(project_name)_login_helper', + 'product_name': '<(product_name) Login Helper', + 'type': 'executable', + 'sources': [ + '<@(login_helper_sources)', + ], + 'include_dirs': [ + '.', + 'vendor', + '<(libchromiumcontent_src_dir)', + ], + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/AppKit.framework', + ], + }, + 'mac_bundle': 1, + 'xcode_settings': { + 'ATOM_BUNDLE_ID': 'com.<(company_abbr).<(project_name).loginhelper', + 'INFOPLIST_FILE': 'atom/app/resources/mac/loginhelper-Info.plist', + 'OTHER_LDFLAGS': [ + '-ObjC', + ], + }, + }, # target login_helper ], }], # OS!="mac" ], diff --git a/filenames.gypi b/filenames.gypi index efe5c5f854..f7a3ac145b 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -665,6 +665,9 @@ 'atom/app/atom_library_main.h', 'atom/app/atom_library_main.mm', ], + 'login_helper_sources': [ + 'atom/app/atom_login_helper.mm', + ], 'locales': [ 'am', 'ar', 'bg', 'bn', 'ca', 'cs', 'da', 'de', 'el', 'en-GB', 'en-US', 'es-419', 'es', 'et', 'fa', 'fi', 'fil', 'fr', 'gu', 'he',