Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update app.{set|get}LoginItemSettings(settings) #37244

Merged
merged 4 commits into from Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 20 additions & 19 deletions docs/api/app.md
Expand Up @@ -1278,28 +1278,22 @@ Returns `boolean` - Whether the current desktop environment is Unity launcher.
### `app.getLoginItemSettings([options])` _macOS_ _Windows_

* `options` Object (optional)
* `path` string (optional) _Windows_ - The executable path to compare against.
Defaults to `process.execPath`.
* `args` string[] (optional) _Windows_ - The command-line arguments to compare
against. Defaults to an empty array.
* `type` string (optional) _macOS_ - Can be one of `mainAppService`, `agentService`, `daemonService`, or `loginItemService`. Defaults to `mainAppService`. Only available on macOS 13 and up. See [app.setLoginItemSettings](app.md#appsetloginitemsettingssettings-macos-windows) for more information about each type.
* `serviceName` string (optional) _macOS_ - The name of the service. Required if `type` is non-default. Only available on macOS 13 and up.
* `path` string (optional) _Windows_ - The executable path to compare against. Defaults to `process.execPath`.
* `args` string[] (optional) _Windows_ - The command-line arguments to compare against. Defaults to an empty array.

If you provided `path` and `args` options to `app.setLoginItemSettings`, then you
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 _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 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 not available on [MAS builds][mas-builds].
* `openAsHidden` boolean _macOS_ _Deprecated_ - `true` if the app is set to open as hidden at login. This does not work on macOS 13 and up.
* `wasOpenedAtLogin` boolean _macOS_ _Deprecated_ - `true` if the app was opened at login automatically. This setting is not available on [MAS builds][mas-builds] or on macOS 13 and up.
* `wasOpenedAsHidden` boolean _macOS_ _Deprecated_ - `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 not available on [MAS builds][mas-builds] or on macOS 13 and up.
* `restoreState` boolean _macOS_ _Deprecated_ - `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 not available on [MAS builds][mas-builds] or on macOS 13 and up.
* `status` string _macOS_ - can be one of `not-registered`, `enabled`, `requires-approval`, or `not-found`.
codebytere marked this conversation as resolved.
Show resolved Hide resolved
* `executableWillLaunchAtLogin` boolean _Windows_ - `true` if app is set to open at login and its run key is not deactivated. This differs from `openAtLogin` as it ignores the `args` option, this property will be true if the given executable would be launched at login with **any** arguments.
* `launchItems` Object[] _Windows_
* `name` string _Windows_ - name value of a registry entry.
Expand All @@ -1313,10 +1307,14 @@ Returns `Object`:
* `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) _macOS_ - `true` to open the app as hidden. Defaults to
`false`. The user can edit this setting from the System Preferences so
`app.getLoginItemSettings().wasOpenedAsHidden` should be checked when the app
is opened to know the current value. This setting is not available on [MAS builds][mas-builds].
* `openAsHidden` boolean (optional) _macOS_ _Deprecated_ - `true` to open the app as hidden. Defaults to `false`. The user can edit this setting from the System Preferences so `app.getLoginItemSettings().wasOpenedAsHidden` should be checked when the app is opened to know the current value. This setting is not available on [MAS build
s][mas-builds] or on macOS 13 and up.
* `type` string (optional) _macOS_ - The type of service to add as a login item. Defaults to `mainAppService`. Only available on macOS 13 and up.
* `mainAppService` - The primary application.
* `agentService` - The property list name for a launch agent. The property list name must correspond to a property list in the app’s `Contents/Library/LaunchAgents` directory.
* `daemonService` string (optional) _macOS_ - The property list name for a launch agent. The property list name must correspond to a property list in the app’s `Contents/Library/LaunchDaemons` directory.
* `loginItemService` string (optional) _macOS_ - The property list name for a login item service. The property list name must correspond to a property list in the app’s `Contents/Library/LoginItems` directory.
* `serviceName` string (optional) _macOS_ - The name of the service. Required if `type` is non-default. Only available on macOS 13 and up.
* `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
Expand All @@ -1325,6 +1323,7 @@ Returns `Object`:
* `enabled` boolean (optional) _Windows_ - `true` will change the startup approved registry key and `enable / disable` the App in Task Manager and Windows Settings.
Defaults to `true`.
* `name` string (optional) _Windows_ - value name to write into registry. Defaults to the app's AppUserModelId().
codebytere marked this conversation as resolved.
Show resolved Hide resolved

Set the app's login item settings.

To work with Electron's `autoUpdater` on Windows, which uses [Squirrel][Squirrel-Windows],
Expand All @@ -1349,6 +1348,8 @@ app.setLoginItemSettings({
})
```

For more information about setting different services as login items on macOS 13 and up, see [`SMAppService`](https://developer.apple.com/documentation/servicemanagement/smappservice?language=objc).

### `app.isAccessibilitySupportEnabled()` _macOS_ _Windows_

Returns `boolean` - `true` if Chrome's accessibility support is enabled,
Expand Down
19 changes: 13 additions & 6 deletions shell/browser/api/electron_api_app.cc
Expand Up @@ -85,6 +85,7 @@

#if BUILDFLAG(IS_MAC)
#include <CoreFoundation/CoreFoundation.h>
#include "base/mac/mac_util.h"
#include "shell/browser/ui/cocoa/electron_bundle_mover.h"
#endif

Expand Down Expand Up @@ -364,25 +365,31 @@ struct Converter<Browser::LoginItemSettings> {
dict.Get("path", &(out->path));
dict.Get("args", &(out->args));
#if BUILDFLAG(IS_WIN)
dict.Get("enabled", &(out->enabled));
dict.Get("name", &(out->name));
dict.Get("enabled", &(out->enabled));
#elif BUILDFLAG(IS_MAC)
dict.Get("serviceName", &(out->service_name));
dict.Get("type", &(out->type));
#endif
return true;
}

static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
Browser::LoginItemSettings val) {
auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
dict.Set("openAtLogin", val.open_at_login);
dict.Set("openAsHidden", val.open_as_hidden);
dict.Set("restoreState", val.restore_state);
dict.Set("wasOpenedAtLogin", val.opened_at_login);
dict.Set("wasOpenedAsHidden", val.opened_as_hidden);
#if BUILDFLAG(IS_WIN)
dict.Set("launchItems", val.launch_items);
dict.Set("executableWillLaunchAtLogin",
val.executable_will_launch_at_login);
#elif BUILDFLAG(IS_MAC)
if (base::mac::MacOSMajorVersion() >= 13)
dict.Set("status", val.status);
#endif
dict.Set("openAtLogin", val.open_at_login);
dict.Set("openAsHidden", val.open_as_hidden);
dict.Set("restoreState", val.restore_state);
dict.Set("wasOpenedAtLogin", val.opened_at_login);
dict.Set("wasOpenedAsHidden", val.opened_as_hidden);
return dict.GetHandle();
}
};
Expand Down
12 changes: 8 additions & 4 deletions shell/browser/browser.h
Expand Up @@ -135,7 +135,11 @@ class Browser : public WindowListObserver {
std::u16string path;
std::vector<std::u16string> args;

#if BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_MAC)
std::string type = "mainAppService";
std::string service_name;
std::string status;
#elif BUILDFLAG(IS_WIN)
// used in browser::setLoginItemSettings
bool enabled = true;
std::wstring name;
Expand Down Expand Up @@ -205,9 +209,9 @@ class Browser : public WindowListObserver {
void ApplyForcedRTL();

// Bounce the dock icon.
enum class BounceType {
kCritical = 0, // NSCriticalRequest
kInformational = 10, // NSInformationalRequest
enum class BounceType{
kCritical = 0, // NSCriticalRequest
kInformational = 10, // NSInformationalRequest
};
int DockBounce(BounceType type);
void DockCancelBounce(int request_id);
Expand Down
79 changes: 67 additions & 12 deletions shell/browser/browser_mac.mm
Expand Up @@ -8,6 +8,8 @@
#include <string>
#include <utility>

#import <ServiceManagement/ServiceManagement.h>

#include "base/apple/bridging.h"
#include "base/apple/bundle_locations.h"
#include "base/apple/scoped_cftyperef.h"
Expand All @@ -19,6 +21,7 @@
#include "chrome/browser/browser_process.h"
#include "net/base/mac/url_conversions.h"
#include "shell/browser/badging/badge_manager.h"
#include "shell/browser/javascript_environment.h"
#include "shell/browser/mac/dict_util.h"
#include "shell/browser/mac/electron_application.h"
#include "shell/browser/mac/electron_application_delegate.h"
Expand Down Expand Up @@ -85,6 +88,15 @@ bool CheckLoginItemStatus(bool* is_hidden) {

return true;
}

Browser::LoginItemSettings GetLoginItemSettingsDeprecated() {
Browser::LoginItemSettings settings;
settings.open_at_login = 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();
return settings;
}
#endif

} // namespace
Expand Down Expand Up @@ -367,28 +379,71 @@ bool CheckLoginItemStatus(bool* is_hidden) {
Browser::LoginItemSettings Browser::GetLoginItemSettings(
const LoginItemSettings& options) {
LoginItemSettings settings;
if (options.type != "mainAppService" && options.service_name.empty()) {
gin_helper::ErrorThrower(JavascriptEnvironment::GetIsolate())
.ThrowTypeError("'name' is required when type is not mainAppService");
return settings;
}

#if IS_MAS_BUILD()
settings.open_at_login = platform_util::GetLoginItemEnabled();
const std::string status =
platform_util::GetLoginItemEnabled(options.type, options.service_name);
settings.open_at_login =
status == "enabled" || status == "enabled-deprecated";
if (@available(macOS 13, *))
settings.status = status;
#else
settings.open_at_login = 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();
// If the app was previously set as a LoginItem with the deprecated API,
// we should report its LoginItemSettings via the old API.
LoginItemSettings settings_deprecated = GetLoginItemSettingsDeprecated();
if (@available(macOS 13, *)) {
const std::string status =
platform_util::GetLoginItemEnabled(options.type, options.service_name);
if (status == "enabled-deprecated") {
settings = settings_deprecated;
} else {
settings.open_at_login = status == "enabled";
settings.status = status;
}
} else {
settings = settings_deprecated;
}
#endif
return settings;
}

void Browser::SetLoginItemSettings(LoginItemSettings settings) {
#if IS_MAS_BUILD()
if (!platform_util::SetLoginItemEnabled(settings.open_at_login)) {
LOG(ERROR) << "Unable to set login item enabled on sandboxed app.";
if (settings.type != "mainAppService" && settings.service_name.empty()) {
gin_helper::ErrorThrower(JavascriptEnvironment::GetIsolate())
.ThrowTypeError("'name' is required when type is not mainAppService");
return;
}
#if IS_MAS_BUILD()
platform_util::SetLoginItemEnabled(settings.type, settings.service_name,
settings.open_at_login);
#else
if (settings.open_at_login) {
base::mac::AddToLoginItems(base::apple::MainBundlePath(),
settings.open_as_hidden);
const base::FilePath bundle_path = base::apple::MainBundlePath();
if (@available(macOS 13, *)) {
// If the app was previously set as a LoginItem with the old API, remove it
// as a LoginItem via the old API before re-enabling with the new API.
const std::string status =
platform_util::GetLoginItemEnabled("mainAppService", "");
if (status == "enabled-deprecated") {
base::mac::RemoveFromLoginItems(bundle_path);
if (settings.open_at_login) {
platform_util::SetLoginItemEnabled(settings.type, settings.service_name,
settings.open_at_login);
}
} else {
platform_util::SetLoginItemEnabled(settings.type, settings.service_name,
settings.open_at_login);
}
} else {
base::mac::RemoveFromLoginItems(base::apple::MainBundlePath());
if (settings.open_at_login) {
base::mac::AddToLoginItems(bundle_path, settings.open_as_hidden);
} else {
base::mac::RemoveFromLoginItems(bundle_path);
}
}
#endif
}
Expand Down
7 changes: 5 additions & 2 deletions shell/common/platform_util.h
Expand Up @@ -49,8 +49,11 @@ bool GetFolderPath(int key, base::FilePath* result);
#endif

#if BUILDFLAG(IS_MAC)
bool GetLoginItemEnabled();
bool SetLoginItemEnabled(bool enabled);
std::string GetLoginItemEnabled(const std::string& type,
const std::string& service_name);
bool SetLoginItemEnabled(const std::string& type,
const std::string& service_name,
bool enabled);
#endif

#if BUILDFLAG(IS_LINUX)
Expand Down