Browse Source

Merge pull request #10856 from dittos/mas-login-helper

Implement login helper to manage login item in Mac App Store build
Cheng Zhao 7 years ago
parent
commit
4b8ab8fc97

+ 11 - 0
atom/app/atom_login_helper.mm

@@ -0,0 +1,11 @@
+#import <Cocoa/Cocoa.h>
+
+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;
+}

+ 16 - 0
atom/app/resources/mac/loginhelper-Info.plist

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleIdentifier</key>
+	<string>${ATOM_BUNDLE_ID}</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundleExecutable</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>LSBackgroundOnly</key>
+	<true/>
+</dict>
+</plist>

+ 9 - 0
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 {

+ 5 - 0
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_

+ 27 - 0
atom/common/platform_util_mac.mm

@@ -6,6 +6,7 @@
 
 #import <Carbon/Carbon.h>
 #import <Cocoa/Cocoa.h>
+#import <ServiceManagement/ServiceManagement.h>
 
 #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

+ 10 - 15
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,

+ 17 - 3
docs/tutorial/mac-app-store-submission-guide.md

@@ -45,7 +45,7 @@ has your Team ID as value:
 </plist>
 ```
 
-Then, you need to prepare two entitlements files.
+Then, you need to prepare three entitlements files.
 
 `child.plist`:
 
@@ -77,6 +77,19 @@ Then, you need to prepare two entitlements files.
 </plist>
 ```
 
+`loginhelper.plist`:
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+  <dict>
+    <key>com.apple.security.app-sandbox</key>
+    <true/>
+  </dict>
+</plist>
+```
+
 You have to replace `TEAM_ID` with your Team ID, and replace `your.bundle.id`
 with the Bundle ID of your app.
 
@@ -97,6 +110,7 @@ INSTALLER_KEY="3rd Party Mac Developer Installer: Company Name (APPIDENTITY)"
 # The path of your plist files.
 CHILD_PLIST="/path/to/child.plist"
 PARENT_PLIST="/path/to/parent.plist"
+LOGINHELPER_PLIST="/path/to/loginhelper.plist"
 
 FRAMEWORKS_PATH="$APP_PATH/Contents/Frameworks"
 
@@ -110,6 +124,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 "$LOGINHELPER_PLIST" "$APP_PATH/Contents/Library/LoginItems/$APP Login Helper.app/Contents/MacOS/$APP Login Helper"
+codesign -s "$APP_KEY" -f --entitlements "$LOGINHELPER_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 +178,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

+ 40 - 0
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"
   ],

+ 3 - 0
filenames.gypi

@@ -661,6 +661,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',