Bug 1895744 - Implemented WRL StartupTask for MSIX launch on login r=nrishel

Differential Revision: https://phabricator.services.mozilla.com/D209718
This commit is contained in:
Nipun Shukla
2024-05-15 02:54:55 +00:00
parent c1c74373e9
commit 51e5f06d08
3 changed files with 412 additions and 1 deletions

View File

@@ -75,6 +75,93 @@ interface nsIWindowsShellService : nsISupports
Array<AString> getLaunchOnLoginShortcuts();
/*
* Disables the startup task corresponding to the provided taskId
* to launch upon OS login. The startup task is declared
* within the App Manifest.
*
* If the task was previously disabled by the user this function
* will not re-enable it.
*
* The APIs used within this function are MSIX only and
* will also not work on MINGW.
*
* @param aTaskId Target taskId to enable
*
* @return True if the application was successfully set up to
* launch on OS login.
*
* @throws NS_ERROR_NOT_AVAILABLE
* If used on a non-MSIX build
* @throws NS_ERROR_NOT_IMPLEMENTED
* If used on a MinGW build
* @throws NS_ERROR_NOT_SAME_THREAD
* If called off main thread
* @throws NS_ERROR_FAILURE
* For other types of failures
*/
[implicit_jscontext]
Promise enableLaunchOnLoginMSIXAsync(in AString aTaskId);
/*
* Disables the startup task corresponding to the provided taskId
* to launch upon OS login. The startup task is declared
* within the App Manifest.
*
* The APIs used within this function are MSIX only and
* will also not work on MINGW.
*
* @param aTaskId Target taskId to disable
*
* @return True if the application was successfully disabled from
* launching on OS login.
*
* @throws NS_ERROR_NOT_AVAILABLE
* If used on a non-MSIX build
* @throws NS_ERROR_NOT_IMPLEMENTED
* If used on a MinGW build
* @throws NS_ERROR_NOT_SAME_THREAD
* If called off main thread
* @throws NS_ERROR_FAILURE
* For other types of failures
*/
[implicit_jscontext]
Promise disableLaunchOnLoginMSIXAsync(in AString aTaskId);
/*
* Determines if the startup task corresponding to the provided taskId to
* launch upon OS login is enabled. The startup task is declared
* within the App Manifest. The APIs used within this function are MSIX
* only and will also not work on MINGW.
*
* If the user has disabled the application from launching on login, it
* cannot be re-enabled by the application.
*
* @param aTaskId Target taskId to check status of
*
* @return 0/1/2/3 if the application's OS launch on login is
disabled in settings / disabled / enabled / enabled by policy
*
* @throws NS_ERROR_NOT_AVAILABLE
* If used on a non-MSIX build
* @throws NS_ERROR_NOT_IMPLEMENTED
* If used on a MinGW build
* @throws NS_ERROR_NOT_SAME_THREAD
* If called off main thread
* @throws NS_ERROR_FAILURE
* For other types of failures
*/
cenum LaunchOnLoginEnabledEnumerator : 8 {
LAUNCH_ON_LOGIN_DISABLED_BY_SETTINGS = 0,
LAUNCH_ON_LOGIN_DISABLED = 1,
LAUNCH_ON_LOGIN_ENABLED = 2,
LAUNCH_ON_LOGIN_ENABLED_BY_POLICY = 3,
};
[implicit_jscontext]
Promise getLaunchOnLoginEnabledMSIXAsync(in AString aTaskId);
/*
* Pin the current app to the taskbar. If aPrivateBrowsing is true, the
* Private Browsing version of the app (with a different icon and launch

View File

@@ -26,6 +26,7 @@
#include "nsDirectoryServiceDefs.h"
#include "nsIWindowsRegKey.h"
#include "nsUnicharUtils.h"
#include "nsWindowsHelpers.h"
#include "nsXULAppAPI.h"
#include "mozilla/WindowsVersion.h"
#include "mozilla/dom/Element.h"
@@ -54,6 +55,14 @@ PSSTDAPI PropVariantToString(REFPROPVARIANT propvar, PWSTR psz, UINT cch);
# define UNLEN 256
#else
# include <Lmcons.h> // For UNLEN
# include <wrl.h>
# include <windows.applicationmodel.activation.h>
# include <windows.foundation.h>
using namespace Microsoft::WRL;
using namespace ABI::Windows;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::ApplicationModel;
using namespace Microsoft::WRL::Wrappers;
#endif
#include <comutil.h>
@@ -1907,6 +1916,310 @@ nsWindowsShellService::IsCurrentAppPinnedToTaskbarAsync(
return NS_OK;
}
#ifndef __MINGW32__
# define RESOLVE_AND_RETURN(HOLDER, RESOLVE, RETURN) \
NS_DispatchToMainThread( \
NS_NewRunnableFunction(__func__, [promiseHolder = HOLDER] { \
promiseHolder.get()->get()->MaybeResolve(RESOLVE); \
})); \
return RETURN
# define REJECT_AND_RETURN(HOLDER, REJECT, RETURN) \
NS_DispatchToMainThread( \
NS_NewRunnableFunction(__func__, [promiseHolder = HOLDER] { \
promiseHolder.get()->get()->MaybeReject(REJECT); \
})); \
return RETURN
static void EnableLaunchOnLoginMSIXAsyncImpl(
const nsString& capturedTaskId,
const RefPtr<nsMainThreadPtrHolder<dom::Promise>> promiseHolder) {
ComPtr<IStartupTaskStatics> startupTaskStatics;
HRESULT hr = GetActivationFactory(
HStringReference(RuntimeClass_Windows_ApplicationModel_StartupTask).Get(),
&startupTaskStatics);
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
}
ComPtr<IAsyncOperation<StartupTask*>> getTaskOperation = nullptr;
hr = startupTaskStatics->GetAsync(
HStringReference(capturedTaskId.get()).Get(), &getTaskOperation);
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
}
auto getTaskCallback =
Callback<IAsyncOperationCompletedHandler<StartupTask*>>(
[promiseHolder](IAsyncOperation<StartupTask*>* operation,
AsyncStatus status) -> HRESULT {
if (status != AsyncStatus::Completed) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
}
ComPtr<IStartupTask> startupTask;
HRESULT hr = operation->GetResults(&startupTask);
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
}
ComPtr<IAsyncOperation<StartupTaskState>> enableOperation;
hr = startupTask->RequestEnableAsync(&enableOperation);
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
}
// Set another callback for enabling the startup task
auto enableHandler =
Callback<IAsyncOperationCompletedHandler<StartupTaskState>>(
[promiseHolder](
IAsyncOperation<StartupTaskState>* operation,
AsyncStatus status) -> HRESULT {
StartupTaskState resultState;
HRESULT hr = operation->GetResults(&resultState);
if (SUCCEEDED(hr) && status == AsyncStatus::Completed) {
RESOLVE_AND_RETURN(promiseHolder, true, S_OK);
}
RESOLVE_AND_RETURN(promiseHolder, false, S_OK);
});
hr = enableOperation->put_Completed(enableHandler.Get());
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, hr);
}
return hr;
});
hr = getTaskOperation->put_Completed(getTaskCallback.Get());
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
}
}
static void DisableLaunchOnLoginMSIXAsyncImpl(
const nsString& capturedTaskId,
const RefPtr<nsMainThreadPtrHolder<dom::Promise>> promiseHolder) {
ComPtr<IStartupTaskStatics> startupTaskStatics;
HRESULT hr = GetActivationFactory(
HStringReference(RuntimeClass_Windows_ApplicationModel_StartupTask).Get(),
&startupTaskStatics);
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
}
ComPtr<IAsyncOperation<StartupTask*>> getTaskOperation = nullptr;
hr = startupTaskStatics->GetAsync(
HStringReference(capturedTaskId.get()).Get(), &getTaskOperation);
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
}
auto getTaskCallback =
Callback<IAsyncOperationCompletedHandler<StartupTask*>>(
[promiseHolder](IAsyncOperation<StartupTask*>* operation,
AsyncStatus status) -> HRESULT {
if (status != AsyncStatus::Completed) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
}
ComPtr<IStartupTask> startupTask;
HRESULT hr = operation->GetResults(&startupTask);
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
}
hr = startupTask->Disable();
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
}
RESOLVE_AND_RETURN(promiseHolder, true, S_OK);
});
hr = getTaskOperation->put_Completed(getTaskCallback.Get());
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
}
}
static void GetLaunchOnLoginEnabledMSIXAsyncImpl(
const nsString& capturedTaskId,
const RefPtr<nsMainThreadPtrHolder<dom::Promise>> promiseHolder) {
ComPtr<IStartupTaskStatics> startupTaskStatics;
HRESULT hr = GetActivationFactory(
HStringReference(RuntimeClass_Windows_ApplicationModel_StartupTask).Get(),
&startupTaskStatics);
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
}
ComPtr<IAsyncOperation<StartupTask*>> getTaskOperation = nullptr;
hr = startupTaskStatics->GetAsync(
HStringReference(capturedTaskId.get()).Get(), &getTaskOperation);
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
}
auto getTaskCallback =
Callback<IAsyncOperationCompletedHandler<StartupTask*>>(
[promiseHolder](IAsyncOperation<StartupTask*>* operation,
AsyncStatus status) -> HRESULT {
if (status != AsyncStatus::Completed) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
}
ComPtr<IStartupTask> startupTask;
HRESULT hr = operation->GetResults(&startupTask);
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
}
StartupTaskState state;
hr = startupTask->get_State(&state);
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, E_FAIL);
}
switch (state) {
case StartupTaskState_EnabledByPolicy:
RESOLVE_AND_RETURN(
promiseHolder,
nsIWindowsShellService::LaunchOnLoginEnabledEnumerator::
LAUNCH_ON_LOGIN_ENABLED_BY_POLICY,
S_OK);
break;
case StartupTaskState_Enabled:
RESOLVE_AND_RETURN(
promiseHolder,
nsIWindowsShellService::LaunchOnLoginEnabledEnumerator::
LAUNCH_ON_LOGIN_ENABLED,
S_OK);
break;
case StartupTaskState_DisabledByUser:
case StartupTaskState_DisabledByPolicy:
RESOLVE_AND_RETURN(
promiseHolder,
nsIWindowsShellService::LaunchOnLoginEnabledEnumerator::
LAUNCH_ON_LOGIN_DISABLED_BY_SETTINGS,
S_OK);
break;
default:
RESOLVE_AND_RETURN(
promiseHolder,
nsIWindowsShellService::LaunchOnLoginEnabledEnumerator::
LAUNCH_ON_LOGIN_DISABLED,
S_OK);
}
});
hr = getTaskOperation->put_Completed(getTaskCallback.Get());
if (FAILED(hr)) {
REJECT_AND_RETURN(promiseHolder, NS_ERROR_FAILURE, /* void */);
}
}
NS_IMETHODIMP
nsWindowsShellService::EnableLaunchOnLoginMSIXAsync(
const nsAString& aTaskId, JSContext* aCx,
/* out */ dom::Promise** aPromise) {
if (!widget::WinUtils::HasPackageIdentity()) {
return NS_ERROR_NOT_AVAILABLE;
}
if (!NS_IsMainThread()) {
return NS_ERROR_NOT_SAME_THREAD;
}
ErrorResult rv;
RefPtr<dom::Promise> promise =
dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
if (MOZ_UNLIKELY(rv.Failed())) {
return rv.StealNSResult();
}
// A holder to pass the promise through the background task and back to
// the main thread when finished.
auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
"EnableLaunchOnLoginMSIXAsync promise", promise);
NS_DispatchBackgroundTask(NS_NewRunnableFunction(
"EnableLaunchOnLoginMSIXAsync",
[taskId = nsString(aTaskId), promiseHolder] {
EnableLaunchOnLoginMSIXAsyncImpl(taskId, promiseHolder);
}));
promise.forget(aPromise);
return NS_OK;
}
NS_IMETHODIMP
nsWindowsShellService::DisableLaunchOnLoginMSIXAsync(
const nsAString& aTaskId, JSContext* aCx,
/* out */ dom::Promise** aPromise) {
if (!widget::WinUtils::HasPackageIdentity()) {
return NS_ERROR_NOT_AVAILABLE;
}
if (!NS_IsMainThread()) {
return NS_ERROR_NOT_SAME_THREAD;
}
ErrorResult rv;
RefPtr<dom::Promise> promise =
dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
if (MOZ_UNLIKELY(rv.Failed())) {
return rv.StealNSResult();
}
// A holder to pass the promise through the background task and back to
// the main thread when finished.
auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
"DisableLaunchOnLoginMSIXAsync promise", promise);
NS_DispatchBackgroundTask(NS_NewRunnableFunction(
"DisableLaunchOnLoginMSIXAsync",
[taskId = nsString(aTaskId), promiseHolder] {
DisableLaunchOnLoginMSIXAsyncImpl(taskId, promiseHolder);
}));
promise.forget(aPromise);
return NS_OK;
}
NS_IMETHODIMP
nsWindowsShellService::GetLaunchOnLoginEnabledMSIXAsync(
const nsAString& aTaskId, JSContext* aCx,
/* out */ dom::Promise** aPromise) {
if (!widget::WinUtils::HasPackageIdentity()) {
return NS_ERROR_NOT_AVAILABLE;
}
if (!NS_IsMainThread()) {
return NS_ERROR_NOT_SAME_THREAD;
}
ErrorResult rv;
RefPtr<dom::Promise> promise =
dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
if (MOZ_UNLIKELY(rv.Failed())) {
return rv.StealNSResult();
}
// A holder to pass the promise through the background task and back to
// the main thread when finished.
auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
"GetLaunchOnLoginEnabledMSIXAsync promise", promise);
NS_DispatchBackgroundTask(NS_NewRunnableFunction(
"GetLaunchOnLoginEnabledMSIXAsync",
[taskId = nsString(aTaskId), promiseHolder] {
GetLaunchOnLoginEnabledMSIXAsyncImpl(taskId, promiseHolder);
}));
promise.forget(aPromise);
return NS_OK;
}
#else
NS_IMETHODIMP
nsWindowsShellService::EnableLaunchOnLoginMSIXAsync(
const nsAString& aTaskId, JSContext* aCx,
/* out */ dom::Promise** aPromise) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsWindowsShellService::DisableLaunchOnLoginMSIXAsync(
const nsAString& aTaskId, JSContext* aCx,
/* out */ dom::Promise** aPromise) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsWindowsShellService::GetLaunchOnLoginEnabledMSIXAsync(
const nsAString& aTaskId, JSContext* aCx,
/* out */ dom::Promise** aPromise) {
return NS_ERROR_NOT_IMPLEMENTED;
}
#endif
NS_IMETHODIMP
nsWindowsShellService::ClassifyShortcut(const nsAString& aPath,
nsAString& aResult) {

View File

@@ -10,9 +10,10 @@
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap uap2 uap3 uap10 rescap">
IgnorableNamespaces="uap uap2 uap3 uap5 uap10 rescap">
<Identity Name="@APPX_IDENTITY@" Publisher="@APPX_PUBLISHER@" Version="@APPX_VERSION@" ProcessorArchitecture="@APPX_ARCH@" />
<Properties>
@@ -102,6 +103,16 @@
</com:SurrogateServer>
</com:ComServer>
</com:Extension>
<uap5:Extension
Category="windows.startupTask"
Executable="VFS\ProgramFiles\@APPX_INSTDIR@\@MOZ_APP_NAME@.exe"
EntryPoint="Windows.FullTrustApplication"
uap10:Parameters="-os-autostart">
<uap5:StartupTask
TaskId="LaunchOnLogin"
Enabled="false"
DisplayName="@MOZ_APP_DISPLAYNAME@" />
</uap5:Extension>
<desktop:Extension Category="windows.toastNotificationActivation">
<desktop:ToastNotificationActivation ToastActivatorCLSID="@MOZ_INOTIFICATIONACTIVATION_CLSID@" />
</desktop:Extension>