Browse Source

fix: natively implement LoginItems methods (#15128)

* fix: natively implement LoginItems methods

* fix flaky spec on MAS builds
Shelley Vohr 6 years ago
parent
commit
f6b7f547bb
2 changed files with 90 additions and 23 deletions
  1. 60 1
      atom/browser/browser_mac.mm
  2. 30 22
      spec/api-app-spec.js

+ 60 - 1
atom/browser/browser_mac.mm

@@ -217,6 +217,65 @@ Browser::LoginItemSettings Browser::GetLoginItemSettings(
   return settings;
 }
 
+// copied from GetLoginItemForApp in base/mac/mac_util.mm
+LSSharedFileListItemRef GetLoginItemForApp() {
+  base::ScopedCFTypeRef<LSSharedFileListRef> login_items(
+      LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL));
+  if (!login_items.get()) {
+    LOG(ERROR) << "Couldn't get a Login Items list.";
+    return NULL;
+  }
+  base::scoped_nsobject<NSArray> login_items_array(
+      base::mac::CFToNSCast(LSSharedFileListCopySnapshot(login_items, NULL)));
+  NSURL* url = [NSURL fileURLWithPath:[base::mac::MainBundle() bundlePath]];
+  for (NSUInteger i = 0; i < [login_items_array count]; ++i) {
+    LSSharedFileListItemRef item =
+        reinterpret_cast<LSSharedFileListItemRef>(login_items_array[i]);
+    CFURLRef item_url_ref = NULL;
+    if (LSSharedFileListItemResolve(item, 0, &item_url_ref, NULL) == noErr &&
+        item_url_ref) {
+      base::ScopedCFTypeRef<CFURLRef> item_url(item_url_ref);
+      if (CFEqual(item_url, url)) {
+        CFRetain(item);
+        return item;
+      }
+    }
+  }
+  return NULL;
+}
+
+void RemoveFromLoginItems() {
+  base::ScopedCFTypeRef<LSSharedFileListRef> list(
+      LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL));
+  if (!list) {
+    LOG(ERROR) << "Unable to access shared file list";
+    return;
+  }
+
+  if (GetLoginItemForApp() != NULL) {
+    base::scoped_nsobject<NSArray> login_items_array(
+        base::mac::CFToNSCast(LSSharedFileListCopySnapshot(list, NULL)));
+
+    if (!login_items_array) {
+      LOG(ERROR) << "No items in list of auto-loaded apps";
+      return;
+    }
+
+    for (NSUInteger i = 0; i < [login_items_array count]; ++i) {
+      LSSharedFileListItemRef item =
+          reinterpret_cast<LSSharedFileListItemRef>(login_items_array[i]);
+      CFURLRef url_ref = NULL;
+      if (LSSharedFileListItemResolve(item, 0, &url_ref, NULL) == noErr &&
+          item) {
+        base::ScopedCFTypeRef<CFURLRef> url(url_ref);
+        if ([[base::mac::CFToNSCast(url.get()) path]
+                hasPrefix:[[NSBundle mainBundle] bundlePath]])
+          LSSharedFileListItemRemove(list, item);
+      }
+    }
+  }
+}
+
 void Browser::SetLoginItemSettings(LoginItemSettings settings) {
 #if defined(MAS_BUILD)
   platform_util::SetLoginItemEnabled(settings.open_at_login);
@@ -224,7 +283,7 @@ void Browser::SetLoginItemSettings(LoginItemSettings settings) {
   if (settings.open_at_login)
     base::mac::AddToLoginItems(settings.open_as_hidden);
   else
-    base::mac::RemoveFromLoginItems();
+    RemoveFromLoginItems();
 #endif
 }
 

+ 30 - 22
spec/api-app-spec.js

@@ -455,31 +455,13 @@ describe('app module', () => {
       app.setLoginItemSettings({openAtLogin: false, path: updateExe, args: processStartArgs})
     })
 
-    it('returns the login item status of the app', done => {
-      app.setLoginItemSettings({openAtLogin: true})
-      expect(app.getLoginItemSettings()).to.deep.equal({
-        openAtLogin: true,
-        openAsHidden: false,
-        wasOpenedAtLogin: false,
-        wasOpenedAsHidden: false,
-        restoreState: false
-      })
-
-      app.setLoginItemSettings({openAtLogin: true, openAsHidden: true})
-      expect(app.getLoginItemSettings()).to.deep.equal({
-        openAtLogin: true,
-        openAsHidden: process.platform === 'darwin' && !process.mas, // Only available on macOS
-        wasOpenedAtLogin: false,
-        wasOpenedAsHidden: false,
-        restoreState: false
-      })
-
-      app.setLoginItemSettings({})
+    it('sets and returns the app as a login item', done => {
+      app.setLoginItemSettings({ openAtLogin: true })
       // Wait because login item settings are not applied immediately in MAS build
-      const delay = process.mas ? 100 : 0
+      const delay = process.mas ? 150 : 0
       setTimeout(() => {
         expect(app.getLoginItemSettings()).to.deep.equal({
-          openAtLogin: false,
+          openAtLogin: true,
           openAsHidden: false,
           wasOpenedAtLogin: false,
           wasOpenedAsHidden: false,
@@ -489,6 +471,32 @@ describe('app module', () => {
       }, delay)
     })
 
+    it('adds a login item that loads in hidden mode', () => {
+      app.setLoginItemSettings({ openAtLogin: true, openAsHidden: true })
+      expect(app.getLoginItemSettings()).to.deep.equal({
+        openAtLogin: true,
+        openAsHidden: process.platform === 'darwin' && !process.mas, // Only available on macOS
+        wasOpenedAtLogin: false,
+        wasOpenedAsHidden: false,
+        restoreState: false
+      })
+    })
+
+    it('correctly sets and unsets the LoginItem as hidden', function () {
+      if (process.platform !== 'darwin' || process.mas) this.skip()
+
+      expect(app.getLoginItemSettings().openAtLogin).to.be.false()
+      expect(app.getLoginItemSettings().openAsHidden).to.be.false()
+
+      app.setLoginItemSettings({ openAtLogin: true, openAsHidden: true })
+      expect(app.getLoginItemSettings().openAtLogin).to.be.true()
+      expect(app.getLoginItemSettings().openAsHidden).to.be.true()
+
+      app.setLoginItemSettings({ openAtLogin: true, openAsHidden: false })
+      expect(app.getLoginItemSettings().openAtLogin).to.be.true()
+      expect(app.getLoginItemSettings().openAsHidden).to.be.false()
+    })
+
     it('allows you to pass a custom executable and arguments', function () {
       if (process.platform !== 'win32') {
         // FIXME(alexeykuzmin): Skip the test.